为什么Object提供equals和hash code方法?

ser*_*0ne 3 c# java language-agnostic

许多面向对象的编程语言(其中类似的东西Object是类型层次结构的根)提供了一些默认的相等和哈希码方法;例如:

  • C# System.ObjectEqualsGetHashCode
  • 爪哇java.lang.ObjectequalshashCode

对于表现出值语义的对象重写这些方法是很有价值的;例如,BooleanIntegerGuid,但为表现出行为语义的对象重写这些方法并不常见;例如服务和控制器。这些方法还用作确定重复项Set<T>Map<K, V>插入的机制。

假设Object不包含这些方法,而是由接口提供;例如:

interface Equatable<T> {
    Boolean equals(T other)
}

interface Hashable {
    Integer getHashCode()
}
Run Code Online (Sandbox Code Playgroud)

现在,表现出行为语义的对象不会实现这些方法,因为它们不是必需的,但表现出值语义的对象可以实现它们;例如:

class Guid : Equatable<Guid>, Hashable {
    public Boolean equals(Guid other) {
        ...
    }

    public Integer getHashCode() {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

并且Set<T>andMap<K, V>可以声明对Tand的约束K,这样对象就应该实现Equatable<T>and Hashable;例如:

interface Set<T> where T extends Equatable<T>, Hashable {
    ...
}

interface Map<K, V> where K extends Equatable<T>, Hashable {
    ...
}
Run Code Online (Sandbox Code Playgroud)

鉴于并非所有对象(即表现出行为语义的对象)都需要重写这些方法,似乎不需要在类型层次结构的根部声明 equals 和 hash code 方法,那么为什么它们定义在类型层次结构的根,而不是预期按要求实现的东西?

这些方法是在类型层次结构的根部实现的,是否还有其他一些潜在或根本原因,是否有任何类似的语言不是这种情况?

Jon*_*asH 5

出于您提到的所有原因,没有根本原因必须从根本上实施它们。这个答案将专门针对 c#,但我猜许多参数也适用于 java。

我们只能推测语言设计者为什么使用这种模型,但这很可能是受到 C# 第一个版本中缺乏泛型的影响。同样缺乏泛型的 java 的影响可能也起了一定作用。请注意,在 c# 中,您建议的接口确实以IEquatable<T>.

但是,如果我正确理解你的论点,HashSet将被限制为IEquatable<T>。这仅适用于使用值相等的类型。因此HashSet不能与使用引用相等的类型一起使用,这似乎是一个明显的问题。字典和哈希集对于使用引用相等的对象仍然非常有用。

这并不是说我认为现在的平等制度设计得很好。要比较两个对象,您可以使用

  1. .Equals(object obj)
  2. IEquatable<T>.Equals(T obj)
  3. 相等运算符==/ !=,(每个运算符都需要显式实现)
  4. IEqualityComparer<T>.Equals(T a, T b)

这会让新开发人员感到困惑,并且往往会导致大量的样板代码。与许多其他事情一样,这部分是由于语言的演变造成的。新功能必须与现有代码一起仔细设计。如果从头开始重写 c#,您几乎肯定可以做出改进,这可能包括删除或重新设计.Equals(object obj). 但这实际上并不可行,因为它会破坏数十年的现有代码。

如果您正在设计一种新语言,请务必考虑如何更好地处理平等问题。但我预计这需要一些主要的设计工作,使其尽可能易于理解和使用,同时仍然允许必要的灵活性。设计语言并不容易!看看其他语言,有几种在平等方面存在问题,C# 至少比javascript好。

至于类似的语言,java 和 c# 在某种意义上都是更容易使用的 c++。这只是通过根本没有通用类型根来避免这个问题。但这种方法存在一些可用性方面的缺点。