Làm cách nào để sao chép một trường hợp lớp trường hợp và thay đổi chỉ một trường trong Scala?


208

Giả sử tôi có một lớp tình huống đại diện cho personas, mọi người trên các mạng xã hội khác nhau. Thể hiện của lớp đó là hoàn toàn bất biến, và được tổ chức trong các bộ sưu tập bất biến, cuối cùng sẽ được sửa đổi bởi một diễn viên Akka.

Bây giờ, tôi có một lớp trường hợp với nhiều trường và tôi nhận được một thông báo nói rằng tôi phải cập nhật một trong các trường, đại loại như thế này:

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)

Lưu ý tôi phải chỉ định tất cả các trường, mặc dù chỉ có một thay đổi. Có cách nào để sao chép hiện tạiPersona và chỉ thay thế một trường mà không chỉ định tất cả các trường không thay đổi không? Tôi có thể viết nó như một đặc điểm và sử dụng nó cho tất cả các lớp trường hợp của tôi không?

Nếu Persona là một ví dụ giống như Bản đồ, nó sẽ dễ thực hiện.

Câu trả lời:


324

case classđi kèm với một copyphương pháp dành riêng cho việc sử dụng này:

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

5
Tài liệu đó ở đâu? Tôi không thể tìm thấy một tài liệu tham khảo để sao chép trong các điểm "rõ ràng", ví dụ scala-lang.org/api/civerse/index.html .
François Beausoleil

6
Đó là một tính năng của ngôn ngữ, bạn có thể tìm thấy nó trong đặc tả Scala: scala-lang.org/docu/files/ScalaReference.pdf §5.3.2. Nó không có trong API vì nó không phải là một phần của API;)
Nicolas

1
Tôi dự định làm cho ScalaDoc hiển thị các phương thức sao chép khi chúng tồn tại, đó không phải là điều bạn muốn sao?
soc

4
Nó sẽ rất tuyệt Nhưng ở đây, vấn đề của François (nếu tôi đúng) là anh ta không biết rằng anh ta sẽ có một copyphương pháp nếu anh ta tuyên bố a case class.
Nicolas

2
@JonathanNeufeld Bạn sẽ khiến nhiều người bạn trong trại fp thuần túy với tình cảm đó. Tôi có xu hướng đồng ý với bạn.
javadba

46

Kể từ 2.8, các lớp trường hợp Scala có một copyphương thức lợi dụng các tham số được đặt tên / mặc định để thực hiện phép thuật của nó:

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

Bạn cũng có thể tạo một phương thức Personađể đơn giản hóa việc sử dụng:

case class Persona(
  svcName  : String,
  svcId    : String,
  sentMsgs : Set[String]
) {
  def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}

sau đó

val newPersona = existingPersona plusMsg newMsg


0

Cân nhắc sử dụng lenstrong Shapelessthư viện:

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))

Hơn nữa, trong trường hợp bạn có các lớp trường hợp lồng nhau , gettersetter phương thức có thể hơi tẻ nhạt để soạn. Nó sẽ là một cơ hội tốt để đơn giản hóa bằng cách sử dụng thư viện ống kính.

Vui lòng tham khảo:


0

Tôi không muốn bao gồm một thư viện lớn để thực hiện các ống kính phức tạp cho phép bạn đặt các giá trị sâu trong các lớp trường hợp lồng nhau. Hóa ra nó chỉ là một vài dòng trong thư viện 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
  }

Sau đó, bạn có thể tạo các ống kính đặt các giá trị lồng nhau sâu dễ dàng hơn nhiều so với sử dụng tính năng sao chép tích hợp. Đây là một liên kết đến một tập hợp lớn nếu các ống kính phức tạp mà thư viện của tôi sử dụng để đặt các giá trị lồng nhau rất nhiều.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.