首页 文章

如何克隆案例类实例并在Scala中只更改一个字段?

提问于
浏览
197

假设我有一个代表角色的案例类,不同社交网络上的人 . 该类的实例是完全不可变的,并且保存在不可变集合中,最终由Akka actor修改 .

现在,我有一个包含许多字段的case类,我收到一条消息,说我必须更新其中一个字段,如下所示:

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的实例,那么很容易做到 .

4 回答

  • 44

    case class 附带了一个专门针对此用法的 copy 方法:

    val newPersona = existingPersona.copy(sentMessages = 
                       existingPersona.sentMessages + newMessage)
    
  • 300

    从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
    
  • 9
    existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
    
  • 0

    考虑在 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))
    

    此外,如果您有 nested 案例类, gettersetter 方法可能有点单调乏味 . 通过使用镜头库,这将是一个简化的好机会 .

    另请参阅:

相关问题