Создание неизменных экземпляров и изменение копий идиоматическим способом

Я хотел бы условно создавать копии экземпляра объекта в зависимости от информации, внешней по отношению к этому экземпляру. Большая часть информации в копиях будет такой же, как и оригинал, но часть информации потребуется изменить. Эта информация передается между актерами, поэтому мне нужно, чтобы объекты были неизменяемыми, чтобы избежать странного поведения, связанного с параллелизмом. Следующий игрушечный код - простой пример того, с чем я хотел бы помочь.

Если у меня есть следующий код:

case class Container(condition:String,amount:Int,ID:Long)

Я могу сделать следующее:

    val a = new Container("Hello",10,1234567890)
    println("a = " + a)
    val b = a.copy(amount = -5)
    println("b = " + b)
    println("amount in b is " + b.amount)

и вывод

a = Container(Hello,10,1234567890)
b = Container(Hello,-5,1234567890)
amount in b is -5

Я также могу условно создавать копии объекта, выполняя следующие действия:

import scala.Math._
val max = 3     
val c = if(abs(b.amount) >= max) b.copy(amount = max,condition="Goodbye") else if(abs(b.amount) < max) b.copy(amount = abs(b.amount))
println("c = " + c)

Если я установлю сумму в объекте b на -5, то результат будет

c = Container(Goodbye,3,1234567890)

и если я установлю сумму в объекте b на -2, то вывод

c = Container(Hello,2,1234567890)

Однако, когда я пытаюсь распечатать c.amount, он помечается компилятором следующим сообщением

println("amount in c is " + c.amount)

значение суммы не является членом Any

Если я изменю строку создания объекта c на

val c:Container = if(abs(b.amount) >= max) b.copy(amount = max,condition="Goodbye") else if(abs(b.amount) < max) b.copy(amount = abs(b.amount))

Я получаю ошибку компилятора

несоответствие типов; найдено: требуется блок: контейнер

Каков лучший идиоматический способ условного создания неизменяемых экземпляров классов дел путем копирования существующих экземпляров и изменения значения или двух?

Спасибо брюс

1 ответ

Решение

Вы не включаете финал else пункт. Таким образом, тип c является Any - единственный тип, который является супертипом обоих Container а также Unit, где Unit является результатом не включая всеобъемлющее else оговорка Если вы попытаетесь заставить тип результата быть Container, написав c: Container = Теперь компилятор сообщает вам, что пропал без вести else оговорка, приводящая к Unit не присваивается Container,

таким образом

val c = if (abs(b.amount) >= max) {
  b.copy(amount = max, condition = "Goodbye")
} else if (abs(b.amount) < max) {
  b.copy(amount = abs(b.amount))
} else b // leave untouched !

работает. Компилятор не достаточно умен, чтобы понять, что последний else пункт не может быть достигнут логически (нужно знать, что abs а также >= а также < означает, что они являются взаимоисключающими и исчерпывающими, и что abs чисто функциональный, как есть b.amount).

Другими словами, поскольку вы знаете, что эти два предложения являются взаимоисключающими и исчерпывающими, вы можете упростить

val c = if (abs(b.amount) >= max) {
  b.copy(amount = max, condition = "Goodbye")
} else { // i.e. abs(b.amount) < max
  b.copy(amount = abs(b.amount))
}
Другие вопросы по тегам