创建不可变实例并以惯用方式修改副本

时间:2011-04-05 16:28:12

标签: class scala copy immutability case

我想根据该实例外部的信息有条件地创建对象实例的副本。副本中的大多数信息将与原始信息相同,但某些信息需要更改。这些信息是在actor之间传递的,所以我需要对象是不可变的,以避免奇怪的并发相关行为。以下玩具代码是我想要帮助的一个简单示例。

如果我有以下代码:

case class Container(condition:String,amount:Int,ID:Long)

我可以做以下事情:

    val a = new Container("Hello",10,1234567890)
    println("a = " + a)
    val b = a.copy(amount = -5)
    println("b = " + b)
    println("amount in b is " + b.amount)

,输出

a = Container(Hello,10,1234567890)
b = Container(Hello,-5,1234567890)
amount in b is -5

我还可以有条件地创建对象的副本,执行以下操作:

import scala.Math._
val max = 3     
val c = if(abs(b.amount) >= max) b.copy(amount = max,condition="Goodbye") else if(abs(b.amount) < max) b.copy(amount = abs(b.amount))
println("c = " + c)

如果我将b对象中的数量设置为-5,则输出为

c = Container(Goodbye,3,1234567890)

如果我将b对象中的数量设置为-2,则输出为

c = Container(Hello,2,1234567890)

然而,当我尝试打印出c.amount时,它会被编译器标记为以下消息

println("amount in c is " + c.amount)
  

值金额不是任何

的成员

如果我将c对象创建行更改为

val c:Container = if(abs(b.amount) >= max) b.copy(amount = max,condition="Goodbye") else if(abs(b.amount) < max) b.copy(amount = abs(b.amount))

我收到编译错误

  

类型不匹配;发现:所需单位:   容器

通过复制现有实例并修改一两个值,有条件地创建案例类的不可变实例的最佳惯用方法是什么?

谢谢, 布鲁斯

1 个答案:

答案 0 :(得分:12)

您未包含最终else条款。因此c的类型是Any - ContainerUnit的唯一类型,其中Unit的结果不包括全包else条款。如果您尝试强制结果类型为Container,编写c: Container =,编译器现在会告诉您else子句导致Unit无法分配给Container 1}}。

因此

val c = if (abs(b.amount) >= max) {
  b.copy(amount = max, condition = "Goodbye")
} else if (abs(b.amount) < max) {
  b.copy(amount = abs(b.amount))
} else b // leave untouched !

的工作原理。编译器不够聪明,无法确定无法逻辑地达到最后的else子句(需要知道abs>=以及<的含义,它们是相互排斥和详尽的,abs纯粹是功能性的,b.amount}也是如此。

换句话说,由于知道这两个条款是相互排斥和详尽的,你可以简化

val c = if (abs(b.amount) >= max) {
  b.copy(amount = max, condition = "Goodbye")
} else { // i.e. abs(b.amount) < max
  b.copy(amount = abs(b.amount))
}