为什么Java的Area#equals方法不会覆盖Object#equals?
我刚遇到Java java.awt.geom.Area#equals(Area)
方法引起的问题。 问题可以简化为以下unit testing:
@org.junit.Test public void testEquals() { java.awt.geom.Area a = new java.awt.geom.Area(); java.awt.geom.Area b = new java.awt.geom.Area(); assertTrue(a.equals(b)); // -> true java.lang.Object o = b; assertTrue(a.equals(o)); // -> false }
经过一些头脑刮擦和调试后,我终于在JDK源代码中看到, Area
equals
方法的签名如下所示:
public boolean equals(Area other)
请注意,它不会从Object
@Override
正常的equals
方法,而只是使用更具体的类型重载方法。 因此,上面示例中的两个调用最终会调用equals
不同实现。
由于Java 1.2以来存在此行为,我认为它不被视为错误。 因此,我更感兴趣的是找出为什么决定不正确地覆盖equals
方法,但同时提供一个重载变量。 (另一个提示,这是一个真正的决定是没有覆盖hashCode()
方法。)
我唯一的猜测是作者担心,在Set
, Map
等中放置Area
s时,缓慢equals
区域的实现不适合比较相等。 数据结构。 (在上面的例子中,你可以添加a
HashSet
,虽然b
等于a
,但调用contains(b)
将失败。)然后,为什么他们不仅仅以不冲突的方式命名可疑方法有这样一个基本概念,如equals
方法?
RealSkeptic在上面的评论中与JDK- 4391558相关联。 该错误中的评论解释了推理:
重写equals(Object)的问题在于,您还必须重写hashCode()以返回一个值,该值仅在两个对象的哈希码也相等时才保证equals()为true。
但:
这里的问题是Area.equals(Area)不执行非常简单的比较。 它仔细检查了两个区域中的每一个几何体,并测试它们是否覆盖相同的封闭空间。 两个Area对象可以对相同的封闭空间有完全不同的描述,equals(Area)会检测到它们是相同的。
所以基本上我们留下了一系列不那么令人愉快的选择,例如:
弃用等于(Area)并为该操作创建备用名称,例如“areasEqual”,以避免混淆。 遗憾的是,旧方法将保留并且可以链接,并且会捕获许多打算调用equals(Object)版本的人。
要么:
弃用equals(Area)并将其实现更改为equals(Object),以避免在调用错误方法时出现语义问题。 创建一个具有不同名称的新方法,以避免混淆实现equals(Area)提供的旧function。
要么:
实现equals(Object)来调用equals(Area)并实现一个虚拟hashCode(),它通过返回一个常量以简并方式来表示equals / hashCode契约。 这将使hashCode方法基本上无用,并使Area对象几乎无用作HashMap或Hashtable中的键。
或修改equals(Area)
行为的其他方法,这些行为会改变其语义或使其与hashCode
不一致。
维护人员认为改变这种方法似乎既不可行(因为bug评论中没有提出的选项都能解决问题)也不重要(因为实现的方法非常慢,并且在比较时可能只会返回true)正如评论者所建议的那样,一个Area
一个实例。
“为什么Java的Area#equals方法不会覆盖Object#equals?”
因为参数具有不同类型的重载方法不需要覆盖。
重写方法将具有与父类中的方法完全相同的方法名称,返回类型,参数数量和参数类型,唯一的区别是方法的定义。
这种情况并不强制我们覆盖,但它会超载,因为它遵循这些规则:
1.)方法的参数数量不同。
2.)参数类型不同(比如将float的参数更改为int)。
“为什么他们不只是以一种不与平等方法这样的基本概念相冲突的方式命名可疑方法?”
因为这可能会使人们走向未来。 如果我们有一台90年代的时间机器,我们可以在没有这个问题的情况下完成它。