Будет ли data class переопределять hashCode и equals если наследуется от кого-то в Kotlin

Ответ

Если data class наследуется от другого класса, он не будет автоматически генерировать equals(), hashCode(), toString() и copy(), если родительский класс объявлен как open или abstract и имеет нефинальные (final в Java) реализации этих методов. Kotlin требует, чтобы родительские методы были final для корректной генерации.

open class Parent {
    open fun equals(other: Any?) = super.equals(other) // Нефинальный метод
}

data class Child(val x: Int) : Parent() // Не сгенерирует equals/hashCode!

Решение:

  1. Сделать родительские методы final (если возможно).
  2. Вручную переопределить equals/hashCode в data class.
  3. Использовать композицию вместо наследования.

Ответ 18+ 🔞

А, слушай, вот тебе реально интересный момент, про который многие наступают на грабли. Смотри, берёшь ты data class, думаешь — красота, всё само сгенерируется, живи да радуйся. Ан нет, ёпта!

Если твой data class вдруг наследуется от какого-то другого класса, который объявлен как open или abstract, и у этого родителя есть нефинальные (то есть не final) реализации методов вроде equals(), hashCode() или toString(), то Kotlin такой: «Ну, братан, извини, но я тут нихуя не сгенерирую». Он же не может просто взять и перебить твои открытые методы, это же пиздец какой-то, нарушение всех правил. Он требует, чтобы родительские методы были final, иначе — всё, приехали.

Вот смотри, наглядный пиздец:

open class Parent {
    open fun equals(other: Any?) = super.equals(other) // Опа, метод открыт, не финальный!
}

data class Child(val x: Int) : Parent() // И всё, привет! Никаких equals/hashCode тут не будет!

Ребёнок (Child) вроде как data class, но по факту он нихуя от этого статуса не получит. Удивление пиздец, да? Сам от себя охуел, когда впервые с этим столкнулся.

Так что делать-то, ёбана?

  1. Идеальный вариант, если можешь. Сделать методы в родительском классе final. Если класс под твоим контролем — просто закрой их нахуй. Но часто так не получается, особенно если родитель из какой-нибудь библиотеки.
  2. Ручная работа, блядь. Если не можешь изменить родителя, придётся в своём data class вручную, по старинке, переопределять equals(), hashCode() и toString(). Веселуха, конечно, весь смысл data class на этом накрывается медным тазом.
  3. Хитрый манёвр. Часто лучший выход — использовать не наследование, а композицию. Вместо «является» сделай «имеет». То есть внутри обычного класса храни экземпляр того, что мог бы быть родителем. Так и грабли обойдёшь, и архитектуру, возможно, получше сделаешь. Но это уже надо думать, э бошка думай!

Короче, запомни: data class и наследование от классов с нефинальными методами — вещи на охуенно тонком льду. Лучше десять раз подумать, прежде чем так делать.