java重写对象。等于VS重载它
阅读:有效Java-Joshua Bloch第二版
第8项-当覆盖等于状态时,遵守总合同:
It is not uncommon for a programmer to write an equals method that looks like this, and then spend hours puzzling over why it doesn't work properly:
[Code sample here]
The problem is that this method does not override Object.equals, whose argument is of type Object, but overloads it instead.
代码示例:
public boolean equals(MyClass o) {
//...
}
我的问题:
为什么像这个代码示例中那样重载的强类型equals方法是不够的?这本书指出重载而不是重写是不好的,但它没有说明为什么会出现这种情况,或者什么情况会使equals方法失败
# 1 楼答案
因为使用equals的集合将使用
Object.equals(Object)
方法(可能在MyClass中被重写,因此以多态方式调用),这与MyClass.equals(MyClass)
不同重载一个方法定义了一个新的、不同的方法,该方法恰好与另一个方法同名
# 2 楼答案
这是因为重载该方法不会改变集合或其他显式使用
equals(Object)
方法的地方的行为。例如,以以下代码为例:如果你把它放在
HashSet
中:这将打印
2
,而不是1
,即使您希望重载中的所有MyClass
实例相等,并且集合不会添加第二个实例因此,基本上,即使这是
true
:这是
false
:后者是集合和其他类用来确定相等性的版本。事实上,仅将返回
true
的地方是参数显式地是MyClass
或其子类型之一的实例编辑:根据您的问题:
覆盖与重载
让我们从重写和重载之间的区别开始。通过重写,实际上可以重新定义方法。您删除了它最初的实现,并用自己的实现替换它。所以当你这么做的时候:
实际上,您正在链接新的
equals
实现,以替换Object
(或上次定义它的任何超类)的实现另一方面,当你这样做:
定义一个全新的方法是因为定义了一个名称相同但参数不同的方法。当
HashSet
调用equals
时,它对类型为Object
的变量调用它:(该代码来自
HashMap.put
的源代码,它被用作HashSet.add
的底层实现。)需要明确的是,只有当
equals
方法被重写而不是重载时,它才会使用不同的equals
。如果试图将@Override
添加到重载的equals
方法中,它将失败,并出现编译器错误,抱怨它没有重写方法。我甚至可以在同一个类中声明两个equals
方法,因为它是重载的:仿制药
至于泛型,
equals
是而不是泛型。它显式地将Object
作为其类型,所以这一点是没有意义的。现在,假设你试着这么做:这不会与以下消息一起编译:
如果你试图
@Override
它:你会得到这个:
所以你赢不了。这里发生的事情是,Java使用擦除实现泛型。当Java在编译时完成对所有泛型类型的检查时,实际的运行时对象都会被替换为
Object
。无论你在哪里看到T
,实际的字节码都包含Object
。这就是为什么反射不能很好地处理泛型类,以及为什么不能执行list instanceof List<String>
之类的操作这也使得您不能重载泛型类型。如果你有这门课:
您将从
add(T)
方法中得到编译器错误,因为当类实际完成编译时,这两个方法将具有相同的签名public void add(Object)
# 3 楼答案
因为它不会覆盖
Object.equals
。任何只知道Object
(例如HashMap
,测试密钥相等性)中声明的方法的通用代码都不会调用重载,它们只会调用提供引用相等性的原始实现记住,重载是在编译时确定的,而重写是在执行时确定的
如果要重写^{,通常最好提供一个强类型版本以及,并从^{中声明的方法委托给它
下面是一个完整的例子,说明它是如何出错的:
修复方法只是添加一个覆盖,如下所示: