提问人:Matthew Layton 提问时间:4/19/2023 更新时间:4/19/2023 访问量:116
为什么 Object 提供 equals 和 hash 代码方法?
Why does Object provide equals and hash code methods?
问:
许多面向对象的编程语言,其中 like 是类型层次结构的根,提供了一些默认的相等和哈希代码方法;例如:Object
- C#:和
System.Object
Equals
GetHashCode
- Java : 和
java.lang.Object
equals
hashCode
对于表现出值语义的对象,重写这些方法很有价值;例如,、 和 ,但对于表现出行为语义的对象,重写这些方法并不常见;例如,服务和控制器。这些方法还用作确定重复项和插入的机制。Boolean
Integer
Guid
Set<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>
T
K
Equatable<T>
Hashable
interface Set<T> where T extends Equatable<T>, Hashable {
...
}
interface Map<K, V> where K extends Equatable<T>, Hashable {
...
}
鉴于并非所有对象(即那些表现出行为语义的对象)都需要重写这些方法,因此,equals 和 hash 代码方法不需要在类型层次结构的根中声明似乎是合理的,那么为什么它们在类型层次结构的根中定义,而不是期望根据需要实现的东西?
这些方法在类型层次结构的根目录下实现是否有其他根本原因,是否有任何类似的语言不是这种情况?
答:
由于您提到的所有原因,没有根本原因必须从根本上实现它们。这个答案将专门针对 c#,但我猜许多参数也适用于 java。
我们只能推测语言设计者为什么使用这个模型,但它很可能是受到 C# 第一个版本中缺少泛型的影响。同样缺乏泛型的 Java 的影响也可能起到了一定作用。请注意,在 c# 中,建议的接口确实以 IEquatable<T>
的形式存在。
但是,如果我正确理解了您的论点,则仅限于.这只会为使用值相等的类型实现。因此,不能与使用引用相等的类型一起使用,这似乎是一个明显的问题。字典和 HashSet 对于使用引用相等的对象仍然非常有用。HashSet
IEquatable<T>
HashSet
这并不是说我认为现在的平等制度是精心设计的。要比较两个对象,您可以使用
.Equals(object obj)
IEquatable<T>.Equals(T obj)
- 相等运算符、/、(每个运算符都需要显式实现)
==
!=
IEqualityComparer<T>.Equals(T a, T b)
这让新开发人员感到困惑,并且往往会导致相当多的样板代码。与许多其他事情一样,这在一定程度上是由于语言的演变。新功能必须经过精心设计,与现有代码配合使用。如果从头开始重写 c#,您几乎可以肯定地进行改进,这可能包括删除或重新设计 .但这并不实际可行,因为它会破坏数十年的现有代码。.Equals(object obj)
如果你正在设计一种新的语言,请考虑如何更好地处理平等。但我预计这需要一些主要的设计工作,以使其尽可能易于理解和使用,同时仍然允许必要的灵活性。设计语言并不容易!环顾其他语言,有几种语言在平等领域存在问题,C# 至少比 javascript 好。
至于类似的语言,java 和 c# 在某种意义上都旨在作为更易于使用的 c++。而这恰恰避免了这个问题,因为根本没有一个通用的类型根。但是这种方法有一些可用性的缺点。
评论
!=
!(a == b)
评论
Object
equals
equals
hashCode
Object
equals
getHashCode
Equatable
和Hashable
。Equals
Object.ReferenceEquals