假设我有一个案例类,它代表不同社交网络上的人物角色。该类的实例是完全不可变的,并保存在不可变集合中,最终由 Akka 演员修改。
现在,我有一个包含许多字段的案例类,我收到一条消息说我必须更新其中一个字段,如下所示:
case class Persona(serviceName : String,
serviceId : String,
sentMessages : Set[String])
// Somewhere deep in an actor
val newPersona = Persona(existingPersona.serviceName,
existingPersona.serviceId,
existingPersona.sentMessages + newMessage)
请注意,我必须指定所有字段,即使只有一个更改。有没有办法克隆 existingPersona 并只替换一个字段,而不指定所有不变的字段?我可以把它写成一个特征并将它用于我所有的案例类吗?
如果 Persona 是一个类似 Map 的实例,那将很容易做到。
case class
附带一个专门用于此用途的 copy
方法:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)
从 2.8 开始,Scala 案例类有一个 copy
方法,该方法利用命名/默认参数来发挥它的魔力:
val newPersona =
existingPersona.copy(sentMessages = existing.sentMessages + newMessage)
您还可以在 Persona
上创建一个方法来简化使用:
case class Persona(
svcName : String,
svcId : String,
sentMsgs : Set[String]
) {
def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}
然后
val newPersona = existingPersona plusMsg newMsg
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
考虑在 Shapeless
库中使用 lens
:
import shapeless.lens
case class Persona(serviceName : String,
serviceId : String,
sentMessages : Set[String])
// define the lens
val messageLens = lens[Persona] >> 'sentMessages
val existingPersona = Persona("store", "apple", Set("iPhone"))
// When you need the new copy, by setting the value,
val newPersona1 = messageLens.set(existingPersona)(Set.empty)
// or by other operation based on current value.
val newPersona2 = messageLens.modify(existingPersona)(_ + "iPad")
// Results:
// newPersona1: Persona(store,apple,Set())
// newPersona2: Persona(store,apple,Set(iPhone, iPad))
此外,如果您有 嵌套 案例类,则 getter
和 setter
方法的编写可能有点乏味。这将是一个很好的机会通过使用镜头库来简化。
另请参考:
适用于任意案例类别的无形 Github / 无样板镜头
快速镜头 Github
scala中的镜头
我不想包含一个大型库来做复杂的镜头,让您在嵌套案例类中设置值。原来它只是 scalaz 库中的几行 code:
/** http://stackoverflow.com/a/5597750/329496 */
case class Lens[A, B](get: A => B, set: (A, B) => A) extends ((A) => B) with Immutable {
def apply(whole: A): B = get(whole)
def mod(a: A, f: B => B) = set(a, f(this (a)))
def compose[C](that: Lens[C, A]) = Lens[C, B](
c => this(that(c)),
(c, b) => that.mod(c, set(_, b))
)
def andThen[C](that: Lens[B, C]) = that compose this
}
然后,您可以创建比使用内置复制功能更容易设置深度嵌套值的镜头。这是一个大集合的链接,如果我的库用来设置大量嵌套值的复杂镜头。
case class
,他将有一个copy
方法。