Considering a case class that has a default field calculated at creating time, is it possible to copy it where the new copied case class has this field recalculated?
For example, TestCopy.auto
is generated at creation time:
case class TestCopy(static: String, auto: Long = System.currentTimeMillis()) {
val auto2 = System.currentTimeMillis()
}
If I copy it changing only the field static
:
val a = TestCopy(static = "a")
val b = a.copy(static = "b")
The field auto
is not recalculated. Neither is the constructor field auto2
:
> println(a)
> println(b)
> println(a.auto2)
> println(b.auto2)
TestCopy(a,1640773176392)
TestCopy(b,1640773176392)
1640773176392
1640773176392
It makes sense, actually, since I am copying the case class. However, I wanted a behaviour similar to recreating the case class, where I change only a set of static fields. The example above is simplified, I could have just created it again. The question is aiming at cases like:
case class TestCopy2(static1: String,
static2: String,
static3: String,
auto: Long = System.currentTimeMillis())
Where only static1
must be changed, static2
and static3
must be copied, and auto
must be generated again.
Is it possible?
Thank you all.
CodePudding user response:
A custom apply
method is viable, but you should also be able to leverage the abstract case class
trick:
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)
}
Making the case class
abstract
suppresses the auto-generated apply
and copy
methods. In this case, the auto-generated apply
is basically exactly what the compiler would generate. The generated copy
would normally be something like
def copy(static: String = static, auto: Long = auto): TestCopy
Note that auto
defaults to the previous value, so that's the behavior we want to change, thus we define a custom copy
method to replace the compiler-generated one.
One of the defaults for auto
can be eliminated by making the constructor private; since case class
es are not typically instantiated with new
, this isn't much of a loss:
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)
}
Now you can:
- create with default current time:
TestCopy("foo")
- create with preset time:
TestCopy("foo", 0)
- copy with current time:
TestCopy("foo").copy(static = "bar")
- copy with preset time:
TestCopy("foo").copy(auto = 0)
CodePudding user response:
Transient values don't get copied automatically:
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