Cast class to data class when using generics

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)
}
2 Likes

@cnorthfleet Have you found a solution?

So your use case is that you want to use the convenience methods of a data class (.copy() to be specific) through a parent of that class?
And since copy doesn’t carry the guarantees of inheritance, you want to use a type check to get access to that copy method with the appropriate properties. Yes?

Your use of copy on the parent class AbstractA may be more suitable as an abstract factory method or some other creational software design pattern.

I suspect this proposed change is closely related to data class inheritance. However, I imagine this issue being solved more elegantly with self-types or type-classes.

I think this request has a lot of value for discussing the use-case but the solution may come from a totally different change than what’s proposed here.

For now you can use the good’ol design patterns since reusing copy as an implicitly inherited method isn’t an easy workaround for data class inheritance.

1 Like