在 Java 中重写 equals 时,为什么使用 Object 以外的参数不起作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/309892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When overriding equals in Java, why does it not work to use a parameter other than Object?
提问by Kip
I ran into an interesting behavior recently. It seems that if I override .equals() to take a parameter other than Object, it doesn't get called. Can anyone explain to me why this is happening? It seems to violate my understanding of polymorphism in OOP, but maybe I'm missing something.
我最近遇到了一个有趣的行为。似乎如果我覆盖 .equals() 以采用 Object 以外的参数,则不会调用它。谁能向我解释为什么会这样?这似乎违反了我对 OOP 中多态性的理解,但也许我错过了一些东西。
Here's much simpler code that shows what I'm seeing:
这是更简单的代码,显示了我所看到的:
public class MyClass {
private int x;
public MyClass(int n) { x = n; }
public boolean equals(Object o) { return false; }
public boolean equals(MyClass mc) { return x == mc.x; }
public static void main(String[] args) {
List<MyClass> list = new ArrayList<MyClass>();
list.add(new MyClass(3));
System.out.println("Contains 3? " + list.contains(new MyClass(3)));
}
}
When this is run, it prints "Contains 3? false". It looks like the equals(Object) function is called, even though there is another that would work. By contrast, if I write equals like this the code works as expected:
运行时,它会打印“ Contains 3? false”。看起来 equals(Object) 函数被调用,即使还有另一个可以工作。相比之下,如果我这样写 equals 代码按预期工作:
public boolean equals(Object o) {
if(!(o instanceof MyClass))
return false;
MyClass mc = (MyClass)o;
return x == mc.x;
}
Why isn't it figuring out which version of the function to call based on the type of the parameter?
为什么不根据参数的类型确定调用哪个版本的函数?
回答by Darron
You're mixing up "overriding" and "overloading".
你混淆了“覆盖”和“重载”。
Overriding -- adding a replacement definition of an existing method for purposes of polymorphism. The method must have the same signature. The signature consists of the name and argument types. Overridden methods are selected at runtime based on the runtime type of the target object.
覆盖——为了多态性的目的添加现有方法的替换定义。该方法必须具有相同的签名。签名由名称和参数类型组成。在运行时根据目标对象的运行时类型选择覆盖方法。
Overloading -- adding a method with the same name but a different signature. Overloaded methods are selected at compile time based on the compile time type of the target object.
重载——添加同名但签名不同的方法。在编译时根据目标对象的编译时类型选择重载方法。
回答by p3t0r
equals(Object) is overriding a super method; you can notoverride a super method without using the exact same signature (Well, there are some exceptions like covariant returntypes and exception).
equals(Object) 正在覆盖一个超级方法;你不能在不使用完全相同的签名的情况下覆盖超级方法(嗯,有一些例外,比如协变返回类型和异常)。
回答by blizpasta
Notice that the method you are calling is defined in the javadoc for ArrayList<E> as
请注意,您正在调用的方法在 ArrayList <E>的 javadoc 中定义为
boolean contains(Object o)
Returns true if this list contains the specified element.
instead of
代替
boolean contains(E o)
Returns true if this list contains the specified element.
Implementation of ArrayList.java:
ArrayList.java 的实现:
private transient Object elementData[];
public boolean contains(Object elem) {
return indexOf(elem) >= 0;
}
public int indexOf(Object elem) {
if (elem == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
It uses the equals method defined in the Object superclass since the equals method is not overridden in ArrayList<E>'s implementation.
它使用 Object 超类中定义的 equals 方法,因为在 ArrayList <E> 的实现中没有覆盖 equals 方法。
When overriding Object equals in java, you should override the Object hashCode method as well.
在 Java 中覆盖 Object equals 时,您还应该覆盖 Object hashCode 方法。
Anyway you might want to try the following code:
无论如何,您可能想尝试以下代码:
class A{
public int content;
A(){
this(0);
}
A(int value){
content = value;
}
public boolean equals(Object obj){
System.out.println("overriding equals method");
return this.content == ((A) obj).content;
}
public boolean equals(A a){
System.out.println("overloading equals method");
return this.content == a.content;
}
public static void main(String[] args){
A x = new A(1);
A y = new A(2);
Object z = new A(1);
System.out.println(x.equals(y));
System.out.println(x.equals(x));
System.out.println(x.equals(z));
//override as z is declared as Object at compile time
//so it will use methods in class Object instead of class A
System.out.println(x.equals((Object) y));
System.out.println(x.equals((Object) x));
}
}
//rant: they didn't teach me these in javaschool and I had to learn it the hard way.
回答by Ray Tayek
there are different types of http://en.wikipedia.org/wiki/Polymorphism_(computer_science). java does not do http://en.wikipedia.org/wiki/Double_dispatch.
有不同类型的http://en.wikipedia.org/wiki/Polymorphism_(computer_science)。java 不做http://en.wikipedia.org/wiki/Double_dispatch。
回答by John Flinchbaugh
The ArrayList implementation of the contains(Object) method is bound to use Object.equals(Object) method internally, so it'll never know about your overloading of the equals(MyClass) method. Only an overriding method (with matching signature) will be found.
contains(Object) 方法的 ArrayList 实现必然在内部使用 Object.equals(Object) 方法,因此它永远不会知道您对 equals(MyClass) 方法的重载。只会找到一个覆盖方法(具有匹配的签名)。
回答by InverseFalcon
You're assuming that the contains()method in Listknows the type of the object at runtime, which is incorrect.
您假设contains()in中的方法在List运行时知道对象的类型,这是不正确的。
Because of erasure, List<MyClass>becomes just a regular Listat runtime, so the contains()method sees its parameter as an Object, thus invoking Object's equals()instead of the one you defined for MyClassin its execution.
由于擦除,在运行时List<MyClass>只是一个常规List,因此该contains()方法将其参数视为Object,从而调用对象equals()而不是您MyClass在执行时为其定义的对象。
回答by Elliot Vargas
Ok let me re-phrase.
好的,让我重新措辞。
(1)Because the compiler eliminates all information regarding to Generics (erasure, see here), and (2) because you cannot override a method without the exact same signature (equals(Object)), (3) during runtime all objects inside the List are treated as Objects and not as instances of MyClass. Hence, the method that gets called is equals(Object) since this is the one that is been overwritten by your class.
(1) 因为编译器消除了与泛型有关的所有信息(擦除,请参见此处),以及 (2) 因为您无法在没有完全相同签名的情况下覆盖方法 (equals(Object)),(3) 在运行时期间内的所有对象列表被视为对象而不是 MyClass 的实例。因此,被调用的方法是 equals(Object),因为这是您的类覆盖的方法。

