Effective Kotlin: Item 10 — Obey the general contract when overriding equals
With item 10 of Effective Java, Joshua Bloch goes into great detail of how to write an excellent equals method and doing the same in Kotlin is no exception.
data class is one of Kotlin’s most touted features as they already implement equals for you. However, one thing to be aware of is that as of Kotlin 1.2 the order in which Kotlin compares properties of a data class follows the order of declaration. Therefore the code below will first compare the name and then the age:
data class User(val name: String, val age: Int)
Joshua suggests that “for best performance, you should first compare fields that are more likely to differ, less expensive to compare, or, ideally, both.” In our example we can achieve this by just changing the order to check the primitive type first:
data class User(val age: Int, val name: String)
Of course, changing the order in your classes may not always make sense, Kotlin does, of course, give you named parameters that will help.
One of the hardest problems to solve with equals is with sub-types where following the Liskov substitution principle “any important property of a type should also hold for all its subtypes so that any method written for the type should work equally well on its subtypes”. Interestingly you cannot extend a data class because “there is no way to make compiler-generated data class methods work consistently and intuitively in case of inheritance.”