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

Kip*_*Kip 11 java overloading

我最近遇到了一个有趣的行为.似乎如果我重写.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)));
  }
}
Run Code Online (Sandbox Code Playgroud)

运行时,它会打印" 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;
}
Run Code Online (Sandbox Code Playgroud)

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

Dar*_*ron 24

你混淆了"覆盖"和"超载".

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

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


p3t*_*t0r 12

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


bli*_*sta 6

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

boolean contains(Object o)
    Returns true if this list contains the specified element. 
Run Code Online (Sandbox Code Playgroud)

代替

boolean contains(E o)
    Returns true if this list contains the specified element. 
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

它使用Object超类中定义的equals方法,因为在ArrayList <E>的实现中没有重写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.
Run Code Online (Sandbox Code Playgroud)