Как клонировать экземпляр класса case и изменить только одно поле в Scala? - PullRequest
202 голосов
/ 31 августа 2011

Допустим, у меня есть класс кейсов, который представляет персон, людей в разных социальных сетях.Экземпляры этого класса являются полностью неизменяемыми и хранятся в неизменяемых коллекциях, которые в конечном итоге будут изменены актером Akka.

Теперь у меня есть класс 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)

Обратите внимание, что я должен указать все поля, хотя только одно из них изменяется.Есть ли способ клонировать существующееPersona и заменить только одно поле, не указывая все поля, которые не меняются?Могу ли я написать это как черту и использовать ее для всех моих классов дел?

Если бы Persona был экземпляром, подобным Map, это было бы легко сделать.

Ответы [ 5 ]

305 голосов
/ 31 августа 2011

case class поставляется с copy методом, который предназначен именно для этого использования:

val newPersona = existingPersona.copy(sentMessages = 
                   existingPersona.sentMessages + newMessage)
45 голосов
/ 31 августа 2011

Начиная с версии 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
10 голосов
/ 31 августа 2011
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
0 голосов
/ 19 апреля 2019

Я не хотел включать большую библиотеку для создания сложных линз, которые позволяют вам устанавливать значения глубоко во вложенных классах дел. Оказывается, это всего лишь несколько строк кода в библиотеке скалаза:

  /** 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
  }

Затем вы можете создавать линзы, которые устанавливают глубоко вложенные значения гораздо проще, чем используя встроенную функцию копирования. Вот ссылка на большой набор сложных линз, которые моя библиотека использует для установки сильно вложенных значений.

0 голосов
/ 20 марта 2019

Рассмотрите возможность использования 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))

Более того, если у вас есть вложенные классы case, методы getter и setter могут быть немного утомительными для составления. Это будет хороший шанс упростить использование библиотеки линз.

Пожалуйста, обратитесь к:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...