假设我有一个类似
的案例类case class Person(fname:String, lname:String, nickName:Option[String] = None)
在创建像Person这样的实例(" John"," Doe")时,我希望nickName自动分配给fname,如果没有给出。例如:
val p1 = Person("John", "Doe")
p1.nickName.get == "John"
val p2 = Person("Jane", "Doe", "Joe")
p2.nickName.get == "Joe"
如何实现从另一个字段自动分配一个字段?
在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))}}
答案 0 :(得分:7)
您可以重载案例类的构造函数
case class Foo(bar: Int, baz: Int) {
def this(bar: Int) = this(bar, 0)
}
new Foo(1, 2)
new Foo(1)
因此,如果nickName为none,您可以检查案例。
你也可以用同样的方式重载它的apply方法。这样,你可以使用
Foo(1,2)
Foo(1)
答案 1 :(得分:4)
在案例类的当前定义中,您可以覆盖案例类的构造函数及其伴随对象的apply方法,如the answer of 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)
}
这是技术上正确且非常聪明的编码。但根据我的口味,它有点过于聪明,因为它打破了一个重要的属性:
CaseClass.unapply(CaseClass.apply(x1,x2,x3)) == (x1,x2,x3)
换句话说:当我使用apply
构造一个带有一些参数元组的案例类,然后使用unapply
解构它时,我希望得到我放入apply
的原始元组(忽略currying和选项类型)。
但在这种情况下,此属性不再适用:
Person.unapply(Person("John", "Smith"))
// result: Option[(String, String, String)] = Some((John,Smith,John))
使用unapply
进行解构用于模式匹配(match{ case ... => ...
)。这是案例类的常见用例。
因此,虽然代码非常聪明,但它可能会混淆其他人并破坏其代码所依赖的属性。
当需要过于聪明的代码时,重新思考一个人试图解决的问题通常是个好主意。在这种情况下,我建议区分用户选择的昵称和系统分配给用户的昵称。在这种情况下,我会建立一个这样的案例类:
case class NickedPerson(fname : String, lname : String, chosenNick : Option[String] = None) {
val nick = chosenNick.getOrElse(fname)
}
然后,您可以使用字段nick
来访问计算的昵称,或者如果您想知道用户是否提供了昵称,请使用chosenNick
:
NickedPerson("John", "Smith").nick
// result: String = "John"
NickedPerson("John", "Smith", Some("Agent")).nick
// result: String = "Agent"
此代码不会更改有关案例类的基本属性。
答案 2 :(得分:2)
仅解释如何从伴侣对象重载apply
(除了@Facundo Fabre答案):
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Person {
def apply(fname:String, lname:String): Person = Person(fname, lname, fname)
}
case class Person(fname:String, lname:String, nickName: String)
// Exiting paste mode, now interpreting.
defined object Person
defined class Person
scala> Person("aaa", "bbb")
res24: Person = Person(aaa,bbb,aaa)
scala> Person("aaa", "bbb", "ccc")
res25: Person = Person(aaa,bbb,ccc)
你也可以使用多参数列表构造函数定义默认值,但是很难使用这样的案例类(没有toString
和模式匹配最后一个参数),所以不推荐(但如果你想要方法的相似之处,它会很好):
scala> case class Person(fname:String, lname:String)(val nickName: String = fname)
defined class Person
另一个有趣的解决方案(只是为了玩),我不建议在实际代码中使用它:
scala> case class Person(fname:String, lname:String, var nickName: String = null){nickName = Option(nickName).getOrElse(fname)}
defined class Person
scala> Person("aaa", "bbb")
res32: Person = Person(aaa,bbb,aaa)