创建不可变实例并以惯用的方式修改副本

发布于 2024-10-30 03:55:00 字数 1526 浏览 4 评论 0原文

我想根据该实例外部的信息有条件地创建对象实例的副本。副本中的大部分信息将与原件相同,但某些信息需要更改。此信息在参与者之间传递,因此我需要对象是不可变的,以避免与并发相关的奇怪行为。以下玩具代码是我想要一些帮助的简单示例。

如果我有以下代码:

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)

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

我收到编译器错误

类型不匹配;找到: 所需单位: 容器

通过复制现有实例并修改一两个值来有条件创建案例类的不可变实例的最佳惯用方法是什么?

谢谢, 布鲁斯

I would like to conditionally create copies of an object instance depending on information external to that instance. Most of the information in the copies will be the same as the original, but some of the information will need to change. This information is being passed around between actors, so I need the objects to be immutable in order to avoid strange concurrency-related behavior. The following toy code is a simple example of what I would like some help with.

If I have the following code:

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

I can do the following:

    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)

and the output is

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

I can also conditionally create copies of the object doing the following:

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)

If I set the amount in the b object to -5, then the output is

c = Container(Goodbye,3,1234567890)

and if I set the amount in the b object to -2, then the output is

c = Container(Hello,2,1234567890)

However, when I try to print out c.amount, it gets flagged by the compiler with the following message

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

value amount is not a member of Any

If I change the c object creation line to

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

I get the compiler error

type mismatch; found: Unit required:
Container

What is the best, idiomatic way of conditionally creating immutable instances of case classes by copying existing instances and modifying a value or two?

Thanks,
Bruce

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ゝ杯具 2024-11-06 03:55:00

您没有包含最后的 else 子句。因此,c 的类型是 Any——唯一同时是 ContainerUnit 超类型的类型,其中Unit不包含包罗万象的else 子句的结果。如果您尝试通过编写 c: Container = 强制结果类型为 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))
}

You are not including a final else clause. Thus the type of c is Any -- the only type that is supertype both of Container and Unit, where Unit is the result of not including a catch-all else clause. If you try to force the result type to be Container, by writing c: Container =, the compiler now tells you the missing else clause resulting in Unit is not assignable to Container.

Thus

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 !

works. The compiler isn't smart enough to figure out that the last else clause cannot be logically reached (it would need to know what abs and >= and < means, that they are mutual exclusive and exhaustive, and that abs is purely functional, as is b.amount).

In other words, since you know that the two clauses are mutually exclusive and exhaustive, you can simplify

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))
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文