如何在案例类中使用另一个字段初始化一个字段?

sco*_*out 7 scala case-class

假设我有一个类似的案例类

case class Person(fname:String, lname:String, nickName:Option[String] = None)
Run Code Online (Sandbox Code Playgroud)

在创建像Person("John","Doe")这样的实例时,我希望将nickName自动分配给fname,如果没有给出.例如:

val p1 = Person("John", "Doe")
p1.nickName.get == "John"

val p2 = Person("Jane", "Doe", "Joe")
p2.nickName.get == "Joe"
Run Code Online (Sandbox Code Playgroud)

如何实现从另一个字段自动分配一个字段?

在repl中尝试以下解决方案.我认为这与repl有关

scala> case class Person(fname: String, lname:String, nickName:Option[String])
defined class Person

scala> object Person { def apply(fname:String, lname:String):Person = {Person(fname, lname, Some(fname))}}
  console:9: error: too many arguments for method apply: (fname: String, lname: String)Person in object Person
   object Person { def apply(fname:String, lname:String):Person = {Person(fname, lname, Some(fname))}}
Run Code Online (Sandbox Code Playgroud)

Fac*_*bre 8

您可以重载案例类的构造函数

case class Foo(bar: Int, baz: Int) {
     def this(bar: Int) = this(bar, 0)
}
new Foo(1, 2)
new Foo(1)
Run Code Online (Sandbox Code Playgroud)

因此,如果nickName为none,您可以检查案例.

您也可以以相同的方式重载它的apply方法.这样,你就可以使用了

Foo(1,2)
Foo(1)
Run Code Online (Sandbox Code Playgroud)


ste*_*hke 5

技术解决方案(不)

在当前的案例类定义中,您可以覆盖案例类的构造函数及其配套对象的apply方法,如Facundo Fabre的答案所述。

您将获得如下内容:

object Person {
  def apply(fname:String, lname:String): Person = Person(fname, lname, fname)
}

case class Person(fname:String, lname:String, nickName: String) {
  def this(fname:String, lname:String) = this(fname, lname, fname)
}
Run Code Online (Sandbox Code Playgroud)

这是技术上正确且非常巧妙的编码。但就我的口味而言,它有点聪明,因为它破坏了一个重要的属性:

CaseClass.unapply(CaseClass.apply(x1,x2,x3)) == (x1,x2,x3)
Run Code Online (Sandbox Code Playgroud)

换句话说:当我使用apply带有一些参数元组的case类,然后使用unapply我对其进行解构时,我希望得到放入的原始元组apply(忽略currying和option type)。

但是在这种情况下,此属性不再成立:

Person.unapply(Person("John", "Smith"))
// result: Option[(String, String, String)] = Some((John,Smith,John))
Run Code Online (Sandbox Code Playgroud)

解构使用unapply用于模式匹配(match{ case ... => ...)。这是案例类的常见用例。

因此,尽管代码非常聪明,但可能会使其他人感到困惑,并破坏他们的代码所依赖的属性。

重述问题(我的建议)

当需要过于聪明的代码时,通常最好重新考虑一下人们试图解决的问题。在这种情况下,我建议您区分用户选择的昵称和系统分配给用户的昵称。在这种情况下,我将只构建一个如下的case类:

case class NickedPerson(fname : String, lname : String, chosenNick : Option[String] = None) {
  val nick = chosenNick.getOrElse(fname)
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以只使用该字段nick来访问计算出的昵称,或者chosenNick如果您想知道用户是否提供了该昵称,请使用:

NickedPerson("John", "Smith").nick
// result: String = "John"
NickedPerson("John", "Smith", Some("Agent")).nick
// result: String = "Agent"
Run Code Online (Sandbox Code Playgroud)

此案例不会更改有关案例类的基本属性。