【问题标题】:When overriding equals in Java, why does it not work to use a parameter other than Object?在Java中覆盖equals时,为什么使用Object以外的参数不起作用?
【发布时间】:2010-09-23 12:27:37
【问题描述】:

我最近遇到了一个有趣的行为。似乎如果我重写 .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) 函数,即使还有另一个可以工作。相比之下,如果我像这样编写 equals 代码,则代码按预期工作:

public boolean equals(Object o) {
  if(!(o instanceof MyClass))
    return false;
  MyClass mc = (MyClass)o;
  return x == mc.x;
}

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

【问题讨论】:

    标签: java overloading


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      好的,让我重新措辞。

      (1)因为编译器消除了与泛型有关的所有信息(擦除,请参阅here),以及 (2) 因为您无法覆盖没有完全相同签名 (equals(Object)) 的方法,(3) 在运行时 List 中的所有对象都被视为对象,而不是 MyClass 的实例。因此,被调用的方法是 equals(Object),因为这是被您的类覆盖的方法。

      【讨论】:

      • 问题与擦除无关。
      • @Darron:不完全正确。如果主要方法是“System.out.println(new MyClass(1).equals(new MyClass(1)));”它会打印“真”。
      • ...但由于擦除,它不知道 equals(MyClass) 方法(它不会覆盖它正在调用的方法)。
      • 具体问题是 List.contains 接收到一个对象,因此调用了覆盖的 Object.equals 方法。我在解释覆盖与重载方面没有问题,但这并不能解决具体问题问题。我的回答可能不完整,但我认为它不值得投反对票。
      【解决方案3】:

      您混淆了“覆盖”和“重载”。

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

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

      【讨论】:

        【解决方案4】:
        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

          由于擦除,List&lt;MyClass&gt; 在运行时只是一个常规的List,所以contains() 方法将其参数视为Object,因此调用Object 的equals() 而不是您为@987654328 定义的那个@ 在其执行中。

          【讨论】:

            【解决方案7】:

            请注意,您调用的方法在 ArrayList&lt;E> 的 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&lt;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.
            

            【讨论】:

              猜你喜欢
              • 2011-02-27
              • 2016-07-12
              • 1970-01-01
              • 2022-12-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-04-11
              • 1970-01-01
              相关资源
              最近更新 更多