在 class 的情况下如何使用另一个字段初始化一个字段?
How to initialize one field using another in case class?
假设我有一个案例class像
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))}}
你可以重载案例的构造函数class
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)
只是解释如何从伴随对象重载 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)
您也可以使用多参数列表构造函数定义默认值,但很难使用这种情况 class(没有 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)
技术方案(不要)
在 case classes 的当前定义中,您可以覆盖 case class 的构造函数及其伴随对象的 apply 方法,如 中所述。
你会得到这样的东西:
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
和一些参数元组构造一个案例 class 然后使用 unapply
解构它时,我希望得到我放入的原始元组apply
(忽略柯里化和选项类型)。
但在这种情况下,这个 属性 不再成立了:
Person.unapply(Person("John", "Smith"))
// result: Option[(String, String, String)] = Some((John,Smith,John))
使用unapply
解构用于模式匹配(match{ case ... => ...
)。这是 case classes.
的常见用例
因此,虽然代码非常聪明,但它可能会混淆其他人并破坏他们的代码所依赖的属性。
重新表述问题(我的建议)
当需要过于聪明的代码时,重新思考试图解决的问题通常是个好主意。在这种情况下,我建议区分用户选择的昵称和系统分配给用户的昵称。在这种情况下,我会像这样构建一个案例 class:
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"
关于 case classes 的基本属性未被此代码更改。
假设我有一个案例class像
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))}}
你可以重载案例的构造函数class
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)
只是解释如何从伴随对象重载 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)
您也可以使用多参数列表构造函数定义默认值,但很难使用这种情况 class(没有 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)
技术方案(不要)
在 case classes 的当前定义中,您可以覆盖 case class 的构造函数及其伴随对象的 apply 方法,如
你会得到这样的东西:
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
和一些参数元组构造一个案例 class 然后使用 unapply
解构它时,我希望得到我放入的原始元组apply
(忽略柯里化和选项类型)。
但在这种情况下,这个 属性 不再成立了:
Person.unapply(Person("John", "Smith"))
// result: Option[(String, String, String)] = Some((John,Smith,John))
使用unapply
解构用于模式匹配(match{ case ... => ...
)。这是 case classes.
因此,虽然代码非常聪明,但它可能会混淆其他人并破坏他们的代码所依赖的属性。
重新表述问题(我的建议)
当需要过于聪明的代码时,重新思考试图解决的问题通常是个好主意。在这种情况下,我建议区分用户选择的昵称和系统分配给用户的昵称。在这种情况下,我会像这样构建一个案例 class:
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"
关于 case classes 的基本属性未被此代码更改。