应该 hashCode() return 一个对象的唯一 ID

Should hashCode() return an object's unique ID

在我的 Kotlin/Java 项目中,我编写了一些继承抽象 class BaseItem:

的模型 classes
/**
 * 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

}

这些模型 classes 将用于表示数据库中的数据。在数据库中,有一个包含唯一整数标识符的 ID 列。

所以当我使用模型classes时,保证每个id属性都是唯一的。

读完 hashCode() 的 Java 规格:

  • Whenever it is invoked on the same object more than once during an execution of an application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

我的问题是:

hashCode() 中 return 这个唯一标识符是好的做法吗?

注意:我知道在 Kotlin 中,我们可以使用 data classes 以便编译器自动派生预定义成员,如 equals()hashCode()toString(),等等,但是 abstract classes 不能是 data classes。 (但是,我可以制作 BaseItem data classes 的 subclasses - 我不确定这是否是这个用例的更好选择) .

因为你的摘要 BaseClass 是针对数据 classes (a.k.a.value classes) 它应该定义 equalshashCode 作为 abstract 并强制执行具体的 classes 来执行它们。例如:

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()

在基础 class 中实现这些功能而不在具体的子 class 中覆盖它们可能会导致违反 equals & hashCode 合同。

如果您不强制子classes 实现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 是根据他们的合同实施的,那么两个相等性检查总是 return 相同的结果(truefalse,在这种情况下,它应该是 false,因为 Product 也应该覆盖这些函数并检查 other 是否也是 Product 并检查每个相关的 属性,等等。 ).

参见 Joshua Bloch 的 Effective Java 第二版 中的 "Item 8: Obey the general contract when overriding equals" 和 "Item 9: Always override hashCode when you override equals",了解有关这些合同和周围问题的更多详细信息分层价值的不同方法 classes.