is it somehow possible to define the copy method g...
# announcements
t
is it somehow possible to define the copy method generated by a data class in an interface? I am trying to figure out a way of refactoring some code that makes extensive use of
copy
and
equals
and being able to have an interface would help me doing the refactor incrementally. eg:
Copy code
interface Test {
  val field: String
  fun copy(field: String)
}

data class TestData(override val field: String) : Test
but ofc this does not compile. I don’t think it is really doable, but hope is the last to die they say 🙂
k
No, this is not allowed by the compiler. the
copy
function is defined by the constructor of the data class and therefor cannot be constrained by an interface. A data class may choose to implement the interface’s field outside of its constructor; the field would never appear in the
copy
function.
You probably have to go by with something like this:
Copy code
interface Test {
    val field: String

    fun withField(field: String): Test
}

data class TestData(override val field: String) : Test {

    override fun withField(field: String): Test = copy(field = field)
}
👍 1
v
The actual problem according to KT-19618 is, that the generated
copy
method has default values for the parameters and this is not allowed for an overwriting method also outside data classes. So in the same sense as
Copy code
interface IFoo {
    fun foo(x: Int, y: Int)
}

class CFoo : IFoo {
    override fun foo(x: Int = 0, y: Int = 1) {
    }
}
is not allowed, it is also not allowed for data classes copy method
k
You’re completely right, that is the technical reason for the compile error. To me it’s about the approach in general, as it is volatile and inconsistent. This case ONLY works for data classes that define the exact set of properties required by the interface in the constructor. Any non-data class or a data class with a deviating set of properties will have to implement the copy function manually, leading to inconsistencies. Adding or removing a property to/from the data class breaks the auto-implementation, making it volatile.
t
yes I know the method is volatile, as I said it would have been nice to help me during a big refactoring, then I was planning to remove the interface. I did not think of looking in KT honestly, it just felt so naturally impossible that I did not think there could have an issue associated
s
Yeah... I wish that Kotlin would have a
data _*interface*_
as well or something similar, that would declare the destructuring operator functions and the copy functions....
2
n
I've wanted something like this in the sense of having have an interface where all properties are val, and then having everything var in the actual data class
v
Copy code
interface Test {
  val field: String
  fun makeCopy(field: String = this.field): Test
}

data class TestData(override var field: String) : Test {
    override fun makeCopy(field: String) = copy(field)
}
image.png
Or you can downcast to copy like
println((test as TestData).copy("jklö"))
s
You can do it manually, but it requires a lot of boiler plate typing. It’d be great if you could do something like this, especially if it involves many properties:
Copy code
data interface Named {
    val firstName: String
    val lastName: String
}

...

data class Person(
    override var firstName: String,
    override var lastName: String,
    var age: Int
): Named

...
val named: Named = Person("Johny", "Matheson", 34)
val copyOfRecord = named.copy(lastName = "R. Matheson")
val (_, _, age) = copyOfRecord
1
n
I eventually gave up on this approach and decided to just use immutable classes + lenses instead
Lenses, with a bit of syntactic sugar I added, make changes to immutable objects pretty comparable to mutation