在我看来,这个概念已经足够可以理解了.
我想做这样的事情(像java一样):
trait PersonInfo {
var name: Option[String] = None
var address: Option[String] = None
// plus another 30 var, for example
}
case class Person() extends PersonInfo
object TestObject {
def main(args: Array[String]): Unit = {
val p = new Person()
p.name = Some("someName")
p.address = Some("someAddress")
}
}
Run Code Online (Sandbox Code Playgroud)
所以我可以更改名称,地址等...
这很好用,但问题是,在我的程序中,我最终将所有内容都视为变量.据我所知,val在scala中是"首选".val如何在这种类型的示例中工作,而不必在每次更改其中一个参数时重写所有30多个参数?
也就是说,我可以
trait PersonInfo {
val name: Option[String]
val address: Option[String]
// plus another 30 val, for example
}
case class Person(name: Option[String]=None, address: Option[String]=None, ...plus another 30.. ) extends PersonInfo
object TestObject {
def main(args: Array[String]): Unit = {
val p = new Person("someName", "someAddress", .....)
// and if I want to change one thing, the address for example
val p2 = new Person("someName", "someOtherAddress", .....)
}
}
Run Code Online (Sandbox Code Playgroud)
这是做事的"正常"scala方式(不能承受22个参数限制)吗?可以看出,我对这一切都很陌生.
首先是Tony K的基本选项:
def withName(n:String)= Person(n,address)
看起来很有前途,但我有很多类扩展了PersonInfo.这意味着在每一个我都必须重新实现defs,大量的打字和切割和粘贴,只是为了做一些简单的事情.如果我将特质PersonInfo转换为普通类并将所有defs放入其中,那么我有一个问题,我怎么能返回Person,而不是PersonInfo?是否有一个聪明的scala事情以某种方式在trait或超类中实现并且所有子类都真正扩展?
据我所知,当示例非常简单,2或3个参数时,scala中的所有工作都非常好,但是当你有几十个参数时,它变得非常乏味且不可行.
Weirdcanada的PersonContext是我认为类似的,还在考虑这个.我想如果我有43个参数,我需要分解成多个临时类,只是为了将参数泵入Person.
复制选项也很有趣,含糊不清但输入的次数要少得多.
来自java我希望scala有一些聪明的技巧.
Lui*_*hys 10
案例类有一个预定义的copy方法,您应该使用它.
case class Person(name: String, age: Int)
val mike = Person("Mike", 42)
val newMike = mike.copy(age = 43)
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?copy只是方法(除了一个equals,hashCode等等),编译器为你写的.在这个例子中它是:
def copy(name: String = name, age: Int = age): Person = new Person(name, age)
Run Code Online (Sandbox Code Playgroud)
值name和age此方法中的值会影响外部作用域中的值.如您所见,提供了默认值,因此您只需指定要更改的值.其他默认为当前实例中的内容.
scala中存在var的原因是支持可变状态.在某些情况下,可变状态确实是您想要的(例如出于性能或清晰度原因).
但是,你是正确的,鼓励使用不可变状态背后有很多证据和经验.事情在许多方面都有效(并发性,理性清晰度等).
你的问题的一个答案是为有问题的类提供mutator方法,这些方法实际上不会改变状态,而是返回一个带有修改条目的新对象:
case class Person(val name : String, val address : String) {
def withName(n : String) = Person(n, address)
...
}
Run Code Online (Sandbox Code Playgroud)
这个特定的解决方案确实涉及编码可能很长的参数列表,但仅限于类本身.用户轻松下载:
val p = Person("Joe", "N St")
val p2 = p.withName("Sam")
...
Run Code Online (Sandbox Code Playgroud)
如果你考虑到你想要改变状态的原因,那么事情会变得更加清晰.如果您正在从数据库中读取数据,则可能有很多原因可以改变对象:
在第一种情况下,不可变状态很容易:
val updatedObj = oldObj.refresh
Run Code Online (Sandbox Code Playgroud)
第二种方法要复杂得多,并且有很多方法可以处理它(包括带有脏字段跟踪的可变状态).它支付给看看像Squery,图书馆,您可以在一个不错的DSL写的东西(见http://squeryl.org/inserts-updates-delete.html),并完全避免使用直接对象突变.
最后一个是您通常希望避免出于复杂性的原因.这样的事情难以并行化,难以推理,并导致各种错误,其中一个类引用另一个类,但不保证它的稳定性.这种用法是我们所讨论的形式的不可变状态的尖叫.