Если hashCode () возвращает уникальный идентификатор объекта

В моем проекте Kotlin / Java я написал несколько классов моделей, наследующих абстрактный класс BaseItem :

 /** * All data model classes should implement this */ abstract class BaseItem { /** * A unique integer identifier used to represent the data item in the database. */ abstract val id: Int override fun equals(other: Any?) = (other is BaseItem) && id == other.id } 

Эти классы моделей будут использоваться для представления данных из базы данных. В базе данных есть столбец идентификатора, который содержит уникальные целочисленные идентификаторы.

Поэтому, когда я использую классы моделей, гарантируется, что свойство id каждого будет уникальным.

После прочтения этой спецификации Java для hashCode() :

  • Всякий раз, когда он вызывается одним и тем же объектом более одного раза во время выполнения приложения, метод hashCode должен последовательно возвращать одно и то же целое число, если информация, используемая при равных сравнениях с объектом, не изменяется. Это целое число не должно оставаться согласованным с одним исполнением приложения на другое выполнение одного и того же приложения.
  • Если два объекта равны в соответствии с методом equals(Object) , то вызов метода hashCode для каждого из двух объектов должен давать одинаковый целочисленный результат.
  • Не требуется, чтобы, если два объекта неравны в соответствии с методом equals(Object) , то вызов метода hashCode для каждого из двух объектов должен производить различные целочисленные результаты. Тем не менее, программист должен знать, что получение отдельных целых результатов для неравных объектов может улучшить производительность хеш-таблиц.

Мой вопрос:

Является ли хорошей практикой возвращать этот уникальный идентификатор в hashCode() ?

Примечание. Я знаю, что в Kotlin мы можем использовать классы data чтобы компилятор автоматически выводил предопределенные члены, такие как equals() , hashCode() , toString() и т. Д., Но abstract классы не могут быть классами data . (Тем не менее, я мог бы создавать подклассы BaseItem data BaseItem – я не уверен, будет ли это лучшим вариантом для этого варианта использования).

    Поскольку ваш абстрактный BaseClass предназначен для классов данных (aka классов значений), он должен определять equals и hashCode как abstract и заставлять реализацию, конкретные классы для их реализации. например:

     abstract class BaseItem { abstract val id: Int abstract override fun equals(other: Any?): Boolean abstract override fun hashCode(): Int } data class Person(override val id: Int, val name: String) : BaseItem() data class Product( override val id: Int, val name: String, val cost: BigDecimal ) : BaseItem() 

    Реализация этих функций в базовом классе, а не переопределение их в конкретных подклассах, может привести к нарушениям контрактов equals & hashCode .

    Вот пример нарушения симметрии, если вы не вынуждаете подклассы реализовать equals / hashCode :

     abstract class BaseItem { abstract val id: Int override fun equals(other: Any?) = (other is BaseItem) && id == other.id override fun hashCode() = id } class Person(override val id: Int, val name: String) : BaseItem() { override fun equals(other: Any?): Boolean { return (other is Person) && id == other.id && name == other.name } override fun hashCode() = 31 * (31 + id.hashCode()) + name.hashCode() } class Product( override val id: Int, val name: String, val cost: BigDecimal ) : BaseItem() fun main(args: Array<String>) { val baseItem1: BaseItem = Person(1, "Oliver") val baseItem2: BaseItem = Product(1, "grease", BigDecimal.TEN) println(baseItem1 == baseItem2) // false println(baseItem2 == baseItem1) // true } 

    Если equals / hashCode были реализованы в соответствии с их контрактами, то обе проверки равенства всегда возвращают один и тот же результат ( true или false , в этом случае он должен быть false как Product также должен переопределять эти функции и проверять, что other также является Product и проверяет каждый соответствующее имущество и т. д.).

    См. «Пункт 8: соблюдайте общий контракт при переопределении равных» и «Пункт 9: Всегда переопределяйте хэш-код, когда вы переопределяете равные» в « Эффективной Java», второе издание Джошуа Блоха, для получения более подробной информации об этих контрактах и ​​проблем, связанных с различными подходами к иерархической ценности классы.