사례 클래스 인스턴스를 복제하고 스칼라에서 하나의 필드 만 변경하는 방법은 무엇입니까?


208

다른 소셜 네트워크에있는 페르소나를 대표하는 사례 클래스가 있다고 가정 해 보겠습니다. 해당 클래스의 인스턴스는 완전히 불변이며 불변 컬렉션에 보관되어 결국 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)

하나만 변경하더라도 모든 필드를 지정해야합니다. 변경되지 않는 모든 필드를 지정하지 않고 기존 개인을 복제하고 하나의 필드 만 바꾸는 방법이 있습니까? 특성으로 작성하여 모든 사례 수업에 사용할 수 있습니까?

페르소나가 맵과 같은 인스턴스라면 쉽게 할 수 있습니다.

답변:


324

case classcopy이 사용법에 정확히 맞는 방법이 제공됩니다.

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

5
그 문서는 어디에 있습니까? scala-lang.org/api/current/index.html 과 같은 "명백한"지점에서 복사 할 참조를 찾을 수 없습니다 .
François Beausoleil

6
언어의 특징이며 scala 사양에서 찾을 수 있습니다 : scala-lang.org/docu/files/ScalaReference.pdf §5.3.2. API의 일부가 아니기 때문에 API에 없습니다.)
Nicolas

1
ScalaDoc에 복사 방법이있을 때 표시하도록하려고했지만 원하는 것이 아닙니까?
soc

4
좋을 것입니다. 그러나 여기서 François의 문제는 (적절한 copy경우) 그가 선언하면 메소드가 있는지 알지 못한다 는 것 case class입니다.
Nicolas

2
@JonathanNeufeld 당신은 그 정서와 함께 순수한 fp 캠프에서 많은 친구를 사 will 것입니다. 나는 당신에게 동의하는 경향이 있습니다.
javadba

46

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


0

라이브러리 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))

또한, 경우에 당신은 중첩 된 경우 클래스의 gettersetter 메소드는 작성하는 데 약간 지루할 수 있습니다. 렌즈 라이브러리를 사용하여 단순화 할 수있는 좋은 기회입니다.

다음도 참조하십시오 :


0

중첩 된 케이스 클래스에서 값을 깊게 설정할 수있는 복잡한 렌즈를 만들기 위해 큰 라이브러리를 포함하고 싶지 않았습니다. 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
  }

그런 다음 내장 된 복사 기능을 사용하는 것보다 훨씬 중첩 된 값을 설정하는 렌즈를 만들 수 있습니다. 내 라이브러리가 복잡한 렌즈를 사용하여 중첩 된 값을 설정하는 경우 큰 세트에 대한 링크가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.