Ответ 1
case class
поставляется с методом copy
, который предназначен именно для этого использования:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)
Скажем, у меня есть класс case, который представляет персонажи, людей в разных социальных сетях. Экземпляры этого класса полностью неизменяемы и хранятся в неизменных коллекциях, которые в конечном итоге будут изменены аккой Аккой.
Теперь у меня есть класс 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 был экземпляром, похожим на карту, это было бы легко сделать.
case class
поставляется с методом copy
, который предназначен именно для этого использования:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)
Начиная с 2.8, Scala классы case имеют метод copy
, который использует свойства named/default для работы своей магией:
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)
Рассмотрите возможность использования lens
в библиотеке Shapeless
:
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
могут быть немного утомительны при составлении. Это будет хороший шанс упростить использование библиотеки линз.
Пожалуйста, также обратитесь к:
Я не хотел включать большую библиотеку для создания сложных линз, которые позволят вам устанавливать значения глубоко во вложенных классах дел. Оказывается, это всего лишь несколько строк кода в библиотеке scalaz:
/** 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
}
Затем вы можете создавать линзы, которые устанавливают глубоко вложенные значения гораздо проще, чем с помощью встроенной функции копирования. Вот ссылка на большой набор сложных линз, которые моя библиотека использует для установки сильно вложенных значений.