¿Cómo clonar una instancia de clase de caso y cambiar solo un campo en Scala?
Digamos que tengo una clase de caso que representa personas, personas en diferentes redes sociales. Las instancias de esa clase son completamente inmutables y se mantienen en colecciones inmutables, que eventualmente serán modificadas por un actor Akka.
Ahora tengo una clase de caso con muchos campos y recibo un mensaje que dice que debo actualizar uno de los campos, algo como esto:
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)
Observe que tengo que especificar todos los campos, aunque solo cambie uno. ¿Hay alguna manera de clonar una Persona existente y reemplazar solo un campo, sin especificar todos los campos que no cambian? ¿Puedo escribir eso como un rasgo y usarlo para todas mis clases de casos?
Si Persona fuera una instancia similar a un mapa, sería fácil de hacer.
case class
viene con un copy
método que está dedicado exactamente a este uso:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)
Desde 2.8, las clases de casos de Scala tienen un copy
método que aprovecha los parámetros con nombre/predeterminados para hacer su magia:
val newPersona =
existingPersona.copy(sentMessages = existing.sentMessages + newMessage)
También puede crear un método Persona
para simplificar el uso:
case class Persona(
svcName : String,
svcId : String,
sentMsgs : Set[String]
) {
def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}
entonces
val newPersona = existingPersona plusMsg newMsg
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
Considere usar lens
en Shapeless
la biblioteca:
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))
Además, en caso de que tenga clases de casos anidadas , los métodos getter
y setter
pueden ser un poco tediosos de componer. Será una buena oportunidad para simplificar utilizando la biblioteca de lentes.
Consulte también:
- Lentes sin forma Github / Boilerplate-free para clases de casos arbitrarios
- Github de lentes rápidas
- Lente en escala