我最近遇到了一个有趣的行为。似乎如果我重写.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<MyClass> list = new ArrayList<MyClass>();
list.add(new MyClass(3));
System.out.println("Contains 3? " + list.contains(new MyClass(3)));
}
}
运行此功能时,会打印“Contains 3? false
”。它看起来像是调用了equals(Object)函数,即使有另一个函数可以工作。相比之下,如果我写这样的等于代码按预期工作:
public boolean equals(Object o) {
if(!(o instanceof MyClass))
return false;
MyClass mc = (MyClass)o;
return x == mc.x;
}
为什么不根据参数的类型确定要调用哪个版本的函数?
答案 0 :(得分:24)
你混淆了“覆盖”和“超载”。
覆盖 - 为了多态而添加现有方法的替换定义。该方法必须具有相同的签名。签名由名称和参数类型组成。在运行时根据目标对象的运行时类型选择重写方法。
重载 - 添加一个名称相同但签名不同的方法。根据目标对象的编译时类型,在编译时选择重载方法。
答案 1 :(得分:12)
equals(Object)覆盖了一个超级方法;您可以不覆盖超级方法而不使用完全相同的签名(好吧,有一些例外,如协变返回类型和异常)。
答案 2 :(得分:6)
请注意,您调用的方法是在javadoc for ArrayList <E
&gt;中定义的。如
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 <E
&gt;的实现中不会覆盖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.
答案 3 :(得分:3)
有http://en.wikipedia.org/wiki/Polymorphism_(computer_science)种不同的类型。 java不做http://en.wikipedia.org/wiki/Double_dispatch。
答案 4 :(得分:2)
contains(Object)方法的ArrayList实现必须在内部使用Object.equals(Object)方法,因此它永远不会知道你的equals(MyClass)方法的重载。只会找到一个重写方法(带有匹配的签名)。
答案 5 :(得分:-1)
好的,让我重新说一句。
(1)因为编译器消除了有关泛型的所有信息(擦除,参见here)和(2),因为你不能覆盖没有完全相同签名的方法(equals(Object)),(3) )在运行期间,List中的所有对象都被视为对象,而不是MyClass的实例。因此,被调用的方法是equals(Object),因为这是你的类被覆盖的方法。
答案 6 :(得分:-1)
您假设contains()
中的List
方法在运行时知道对象的类型,这是不正确的。
由于擦除,List<MyClass>
在运行时变为常规List
,因此contains()
方法将其参数视为Object
,从而调用对象的equals()
而不是你在执行中为MyClass
定义的那个。