Since data class hierarchy is not available, a workaround is to override non-data class hierarchy attributes above the data class, as per the example below:
abstract class AbstractA {
abstract val a: String
}
abstract class AbstractB : AbstractA() {
abstract val b: Int
}
data class DataC(
override val a: String,
val c: Long
) : AbstractA()
data class DataD(
override val a: String,
override val b: Int,
val d: Long
) : AbstractB()
Now, suppose there are several distinct data classes in this small hierarchy. Some deriving from abstract class AbstractA and some from class AbstractB. Suppose I have simple functions, like the ones below, where I do not get the data classes as input parameters but the hierarchy classes (AbstractA and AbstractB). Rationale is that I want those functions to work for all data classes, as they change attributes defined in the abstract classes and overridden in the data classes. Please note I want to define common attributes for data classes, that’s why the hierarchy, AND I also want to maintain immutability in the data classes and use the built in copy
function.
fun <T : AbstractA> changeA(item: T): T {
return item.copy(a = "Hey") // compiler does not know item is a data class
}
fun <T : AbstractB> changeB(item: T) : T {
return item.copy(a = "Hou", b = 10) // compiler does not know item is a data class
}
A mechanism to check if an object’s class is a data class, but what about casting it to a data class? I mean, this is not actually a cast, but making an object of a generic type to assume the role of a data class if indeed it is a data class. Something along the lines of:
fun <T : AbstractB> changeB(item: T) : T {
require(item::class.isData)
return (item as Data).copy(a = "Hou", b = 10)
}