Ответ
Если 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!
Решение:
- Сделать родительские методы
final(если возможно). - Вручную переопределить
equals/hashCodeвdata class. - Использовать композицию вместо наследования.
Ответ 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, но по факту он нихуя от этого статуса не получит. Удивление пиздец, да? Сам от себя охуел, когда впервые с этим столкнулся.
Так что делать-то, ёбана?
- Идеальный вариант, если можешь. Сделать методы в родительском классе
final. Если класс под твоим контролем — просто закрой их нахуй. Но часто так не получается, особенно если родитель из какой-нибудь библиотеки. - Ручная работа, блядь. Если не можешь изменить родителя, придётся в своём
data classвручную, по старинке, переопределятьequals(),hashCode()иtoString(). Веселуха, конечно, весь смыслdata classна этом накрывается медным тазом. - Хитрый манёвр. Часто лучший выход — использовать не наследование, а композицию. Вместо «является» сделай «имеет». То есть внутри обычного класса храни экземпляр того, что мог бы быть родителем. Так и грабли обойдёшь, и архитектуру, возможно, получше сделаешь. Но это уже надо думать, э бошка думай!
Короче, запомни: data class и наследование от классов с нефинальными методами — вещи на охуенно тонком льду. Лучше десять раз подумать, прежде чем так делать.