I'm struggling with data classes and polymorphism. I want to benefit from the immutability, but still be able to update my state. For this, I'm hoping to be able to use the copy
function.
Let's give an example. I have this class hierarchy:
interface Aging {
val age : Int
}
data class Cheese (
override val age : Int
// more Cheese specific properties
) : Aging
data class Wine (
override val age : Int,
val grape : String
// more Wine specific properties
) : Aging
And now I want to be able to do something like this (but this does not work):
class RipeningProcess(){
fun ripen(products : List<Aging>) =
// Not possibe, no copy function on Aging
products.map { it.copy(age = it.age + 1) }
}
How can I create updated copies in a polymorphic way?
I've tried to give the interface a copy
function, but if the subtypes have additional properties they don't override the copy function.
It's frustrating since I know the subtypes have that property, but I cannot utilize that knowledge in the interface.
Data class can implement interfaces and extend to other classes. The parameters of the class can be either val or var type.
Data classes are one of the most used Kotlin features and for a good reason — they decrease the boilerplate code you need to write, enable features like destructuring and copying an object and let you focus on what matters: your app.
Kotlin interfaces are similar to abstract classes. However, interfaces cannot store state whereas abstract classes can. Meaning, interface may have property but it needs to be abstract or has to provide accessor implementations. Whereas, it's not mandatory for property of an abstract class to be abstract.
[OP:] The best I've come up with is indeed to declare the copy function in the interface:
interface Aging {
val age : Int
fun copy(age : Int) : Aging
}
This works out of the box for data class subtypes without additional properties (i.e. Cheese from the question). For data class subtypes with additional properties you need to declare it explicitly, since the generated copy
function does not override the one from the interface.
The subtype with it's age-copy implementation looks like:
data class Wine(
override val age : Int,
val grape : String
) : Aging {
// Different parameter name, to avoid conflict with generated copy method
override fun copy(_age: Int) = copy(age = _age)
}
Hoping for better solutions (or a Kotlin improvement ;) ).
Edit: updated to follow Ghedeons suggestion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With