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