The truth is: data classes do not play too well with inheritance. We are considering prohibiting or severely restricting inheritance of data classes. For example, it's known that there's no way to implement equals() correctly in a hierarchy on non-abstract classes.

So, all I can offer: don't use inheritance with data classes.


Declare properties in super-class outside of constructor as abstract, and override them in sub-class.

abstract class Resource {
    abstract var id: Long
    abstract var location: String
}

data class Book (
    override var id: Long = 0,
    override var location: String = "",
    var isbn: String
) : Resource()

Above solution using abstract class actually generates corresponding class and let the data class extends from it.

If you don't prefer abstract class, how about using an interface?

Interface in Kotlin can have properties as shown in this this article..

interface History {
    val date: LocalDateTime
    val name: String
    val value: Int
}

data class FixedHistory(override val date: LocalDateTime,
                        override val name: String,
                        override val value: Int,
                        val fixedEvent: String) : History

I was curious how Kotlin compile this. Here's equivalent Java code (generated using the Intellij [Kotlin bytecode] feature):

public interface History {
   @NotNull
   LocalDateTime getDate();

   @NotNull
   String getName();

   int getValue();
}

public final class FixedHistory implements History {
   @NotNull
   private final LocalDateTime date;
   @NotNull
   private final String name;
   private int value;
   @NotNull
   private final String fixedEvent;

   // Boring getters/setters as usual..
   // copy(), toString(), equals(), hashCode(), ...
}

As you can see, it works exactly like a normal data class!


Kotlin Traits can help.

interface IBase {
    val prop:String
}

interface IDerived : IBase {
    val derived_prop:String
}

data classes

data class Base(override val prop:String) : IBase

data class Derived(override val derived_prop:String,
                   private val base:IBase) :  IDerived, IBase by base

sample usage

val b = Base("base")
val d = Derived("derived", b)

print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"

This approach can also be a workaround for inheritance issues with @Parcelize

@Parcelize 
data class Base(override val prop:Any) : IBase, Parcelable

@Parcelize // works fine
data class Derived(override val derived_prop:Any,
                   private val base:IBase) : IBase by base, IDerived, Parcelable