在Java中重写equals时,为什么使用Object以外的参数不起作用?

我最近遇到了一个有趣的行为。 似乎如果我重写.equals()来获取除Object之外的参数,它就不会被调用。 任何人都可以向我解释为什么会这样吗? 这似乎违反了我对OOP中多态性的理解,但也许我错过了一些东西。

这里有更简单的代码,显示了我所看到的内容:

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 list = new ArrayList(); list.add(new MyClass(3)); System.out.println("Contains 3? " + list.contains(new MyClass(3))); } } 

运行此命令时,会打印“ 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; } 

为什么不根据参数的类型确定要调用哪个版本的函数?

你混淆了“覆盖”和“超载”。

覆盖 – 为了多态而添加现有方法的替换定义。 该方法必须具有相同的签名。 签名由名称和参数类型组成。 根据目标对象的运行时类型在运行时选择重写的方法。

重载 – 添加具有相同名称但具有不同签名的方法。 在编译时根据目标对象的编译时类型选择重载方法。

equals(Object)覆盖了一个超级方法; 你不能在不使用完全相同的签名的情况下覆盖超级方法(好吧,有一些例外,如协变返回类型和exception)。

请注意,您调用的方法是在ArrayList >的javadoc中定义的

 boolean contains(Object o) Returns true if this list contains the specified element. 

代替

 boolean contains(E o) Returns true if this list contains the specified element. 

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; } 

它使用Object超类中定义的equals方法,因为在ArrayList >的实现中不会覆盖equals方法。

在java中重写Object equals时,您也应该重写Object hashCode方法。

无论如何,您可能想尝试以下代码:

 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. 

有不同类型的http://en.wikipedia.org/wiki/Polymorphism_(computer_science) 。 java不做http://en.wikipedia.org/wiki/Double_dispatch 。

contains(Object)方法的ArrayList实现必须在内部使用Object.equals(Object)方法,因此它永远不会知道你的equals(MyClass)方法的重载。 只会找到一个重写方法(带有匹配的签名)。

好吧让我重新说一句。

(1)因为编译器消除了有关generics的所有信息(擦除,请参见此处 ),以及(2)因为你不能覆盖没有完全相同签名的方法(equals(Object)),(3)在运行时所有对象内部列表被视为对象而不是MyClass的实例。 因此,被调用的方法是equals(Object),因为这是您的类被覆盖的方法。

您假设List中的contains()方法在运行时知道对象的类型,这是不正确的。

由于擦除, List在运行时变为常规List ,因此contains()方法将其参数视为Object ,从而调用Object的equals()而不是您在执行时为MyClass定义的那个。