Difference in behavior of inherited methods between data and regular classes

sealed class Parent {
    override fun toString(): String {
        return "Hello"
    }
}

data class DataChild(val doesNotMatter: String? = null): Parent()

class ClassChild: Parent()

fun main() {
    val dataChild = DataChild()
    val classChild = ClassChild()
    
    println("DataChild.toSting(): ${dataChild.toString()}")
    println("ClassChild.toSting(): ${classChild.toString()}")
}

Output:

DataChild.toSting(): DataChild(doesNotMatter=null)
ClassChild.toSting(): Hello

This is unexpected, and a potential source of error. When a class, data or otherwise, doesn’t override a superclass method, I’d expect the superclass method to be called. That’s how inheritance works.

The compiler automatically derives the following members from all properties declared in the primary constructor:

  • equals() / hashCode() pair;
  • toString() of the form "User(name=John, age=42)" ;
  • componentN() functions corresponding to the properties in their order of declaration;
  • copy() function (see below).

https://kotlinlang.org/docs/reference/data-classes.html

Data classes automatically override the toString method of the parent class. Same way the override equals or hashCode. There are a few exceptions for the use of the autogenerated methods:

If there are explicit implementations of equals() , hashCode() or toString() in the data class body or final implementations in a superclass, then these functions are not generated, and the existing implementations are used

1 Like

Data classes automatically override the toString method of the parent class.

Consider having many data classes all inheriting from a single parent that need to be turned into JSON strings. Given the current design, you’d have to implement toString for each one of them. What a giant pain in the neck!

If there are explicit implementations of equals() , hashCode() or toString() in the data class

I think this should be extended to check the superclasses too, i.e., if someone in the class hierarchy has a custom implementation for one of the aforementioned methods, use that.

Not sure this is possible in case of parent classes that are part of a different .kt file or different jar. In any case this would be a breaking change. It could still be changed but it would probably take quite some time.

Right now you could mark the toString function as final. That way your data classes all use that implementation, but you can no longer create custom versions.

2 Likes

final works, thanks :slight_smile: @Wasabi375 can the docs be updated to mention this “trick”?

It’s in the part I quoted above.

1 Like

You should make the classes serializable and make an extension function like toJson().
Also your question probably arises because you are used to that in Java.

While you could find a workaround to do the same in Kotlin, probably that not the right way to go.

1 Like

If you don’t want the automatically-provided toString() implementation, do you really need a data class?

1 Like