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

Why does Object provide equals and hash code methods?

提问人:Matthew Layton 提问时间:4/19/2023 更新时间:4/19/2023 访问量:116

问:

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

  • C#:和System.ObjectEqualsGetHashCode
  • Java : 和java.lang.ObjectequalshashCode

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

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

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

interface Hashable {
    Integer getHashCode()
}

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

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

    public Integer getHashCode() {
        ...
    }
}

此外,还可以声明对 和 的约束,使得对象应实现 ;例如:Set<T>Map<K, V>TKEquatable<T>Hashable

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

interface Map<K, V> where K extends Equatable<T>, Hashable {
    ...
}

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

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

Java C# 语言无关

评论

5赞 SomeBody 4/19/2023
在 C# 的第一个版本中,没有泛型。也许这就是为什么语言设计者认为将 equals 和 gethashcode 方法添加到对象类会更容易的原因。
1赞 Slaw 4/19/2023
至少在 Java 中,对象具有默认的相等性(即引用相等性)。对我来说,定义.鉴于并具有内在的相关性,对我来说,定义后一种方法也是有意义的。ObjectequalsequalshashCodeObject
0赞 Matthew Layton 4/19/2023
@SomeBody 这是一个很好的观点。我相信 Java 也是如此。在此基础上,您是否认为可以设计一种从一开始就包含泛型的假设性新编程语言,以便可以通过接口来实现?equalsgetHashCode
1赞 Sweeper 4/19/2023
是的。斯威夫特就是这样做的。请参阅 Swift 协议 EquatableHashable
0赞 Matthew Layton 4/19/2023
@Slaw C# 也是如此,其中默认为引用相等,但在 C# 中,也有一个静态 ,所以它似乎没有必要。EqualsObject.ReferenceEquals

答:

5赞 JonasH 4/19/2023 #1

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

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

但是,如果我正确理解了您的论点,则仅限于.这只会为使用值相等的类型实现。因此,不能与使用引用相等的类型一起使用,这似乎是一个明显的问题。字典和 HashSet 对于使用引用相等的对象仍然非常有用。HashSetIEquatable<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++。而这恰恰避免了这个问题,因为根本没有一个通用的类型根。但是这种方法有一些可用性的缺点。

评论

0赞 Ralf 4/19/2023
我放弃了它,“这并不是说我认为 C# 中的平等系统设计得很好”,但不喜欢这个措辞。它设计得很好,然后随着新的想法而发展,你会得到同样的事情的并发概念,因为你不能简单地简单地删除被取代的概念。只有在后视镜中,它看起来像是一个设计问题。不,这是一个进化问题。
0赞 JonasH 4/19/2023
@Ralf公平地说,我做了一些更新。但我认为有些功能,比如没有自动实现,从一开始就是糟糕的设计。因此,我仍然认为这不仅仅是由于语言的演变。!=!(a == b)