ser*_*0ne 3 c# java language-agnostic
许多面向对象的编程语言(其中类似的东西Object
是类型层次结构的根)提供了一些默认的相等和哈希码方法;例如:
System.Object
:Equals
和GetHashCode
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()
}
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>
可以声明对T
and的约束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 方法,那么为什么它们定义在类型层次结构的根,而不是预期按要求实现的东西?
这些方法是在类型层次结构的根部实现的,是否还有其他一些潜在或根本原因,是否有任何类似的语言不是这种情况?
出于您提到的所有原因,没有根本原因必须从根本上实施它们。这个答案将专门针对 c#,但我猜许多参数也适用于 java。
我们只能推测语言设计者为什么使用这种模型,但这很可能是受到 C# 第一个版本中缺乏泛型的影响。同样缺乏泛型的 java 的影响可能也起了一定作用。请注意,在 c# 中,您建议的接口确实以IEquatable<T>
.
但是,如果我正确理解你的论点,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++。这只是通过根本没有通用类型根来避免这个问题。但这种方法存在一些可用性方面的缺点。