【问题标题】:Scala - copy case class with dynamic fields. Is it possible?Scala - 具有动态字段的复制案例类。可能吗?
【发布时间】:2022-01-27 18:08:37
【问题描述】:

考虑到在创建时计算了默认字段的案例类,是否可以将其复制到新复制的案例类重新计算该字段的位置?

例如TestCopy.auto在创建时生成:

  case class TestCopy(static: String, auto: Long = System.currentTimeMillis()) {
    val auto2 = System.currentTimeMillis()
  }

如果我复制它只更改字段static

    val a = TestCopy(static = "a")
    val b = a.copy(static = "b")

字段auto 不会重新计算。构造函数字段auto2也不是:

> println(a)
> println(b)
> println(a.auto2)
> println(b.auto2)

TestCopy(a,1640773176392)
TestCopy(b,1640773176392)
1640773176392
1640773176392

实际上,这是有道理的,因为我正在复制案例类。 但是,我想要一个类似于重新创建案例类的行为,我只更改一组静态字段。 上面的示例已简化,我可以再次创建它。该问题针对以下情况:

  case class TestCopy2(static1: String,
                       static2: String,
                       static3: String,
                       auto: Long = System.currentTimeMillis())

其中只有static1必须更改,static2static3必须复制,auto必须重新生成。 有可能吗?

谢谢大家。

【问题讨论】:

  • 您认为val 是什么意思?是否有任何其他替代关键字可以赋予它不同的行为?那么,当 case class 中的字段被假定为不可变值但您希望它不断变化时,将其作为一个字段是否有意义?
  • 对于这种用法,您必须每次手动实例化一个新的实例,copy 无济于事。不过,您可以创建一个自定义的 apply 方法。
  • @GaëlJ 我不太确定这个问题想要做什么,但自定义 apply 最有意义。
  • @yangzai,当然我们可以使用 var 作为可变变量。但问题是关于将案例类复制到新的类。
  • @YFI 我不是在说var

标签: scala functional-programming copy case-class


【解决方案1】:

自定义apply 方法是可行的,但您也应该能够利用abstract case class 技巧:

abstract case class TestCopy(static: String, auto: Long = System.currentTimeMillis()) {
  def copy(static: String = static, auto: Long = System.currentTimeMillis()): TestCopy =
    new TestCopy(static, auto)
}

object TestCopy {
  def apply(static: String, auto: Long = System.currentTimeMillis()): TestCopy =
    new TestCopy(static, auto)
}

制作case class abstract 会抑制自动生成的applycopy 方法。在这种情况下,自动生成的apply 基本上正是编译器将生成的。生成的copy 通常类似于

def copy(static: String = static, auto: Long = auto): TestCopy

注意auto 默认为之前的值,所以这是我们想要改变的行为,因此我们定义了一个自定义的copy 方法来替换编译器生成的方法。

auto 的默认值之一可以通过将构造函数设为私有来消除;因为case classes 通常不使用new 实例化,所以这并没有太大的损失:

abstract case class TestCopy private (static: String, auto: Long) {
  def copy(static: String = static, auto: Long = System.currentTimeMillis()): TestCopy =
    new TestCopy(static, auto)
}

object TestCopy {
  def apply(static: String, auto: Long = System.currentTimeMillis()): TestCopy =
    new TestCopy(static, auto)
}

现在你可以:

  • 使用默认当前时间创建:TestCopy("foo")
  • 使用预设时间创建:TestCopy("foo", 0)
  • 以当前时间复制:TestCopy("foo").copy(static = "bar")
  • 预设时间复制:TestCopy("foo").copy(auto = 0)

【讨论】:

  • 实际上并没有尝试过:我通常只使用abstract case classsealed,所以我可能会分解sealedabstractcase classes 的作用是错误的。跨度>
【解决方案2】:

瞬态值不会自动复制:

case class Foo(a: Int) { 
   @transient val b: Long = System.currentTimeMillis 
}


val f1 = Foo(1)
Thread.sleep(1000)
val f2 = f1.copy(2)

assert f1.b != f2.b

【讨论】:

  • 为什么会这样? Transient 用于告诉编译器不要序列化 ​​val。那么为什么要在副本中重新计算呢?
  • @YFI 显然也是为了告诉copy 不要复制:) 就是这样。就我个人而言,复制的结果与序列化/反序列化的结果相同是有道理的。
  • 得到你,@Dima。谢谢。
猜你喜欢
  • 2012-10-22
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 2013-07-13
  • 1970-01-01
相关资源
最近更新 更多