Al anular iguales en Java, ¿por qué no funciona usar un parámetro que no sea Object?

StackOverflow https://stackoverflow.com/questions/309892

  •  08-07-2019
  •  | 
  •  

Pregunta

Me encontré con un comportamiento interesante recientemente. Parece que si anulo .equals () para tomar un parámetro que no sea Object, no se llama. ¿Alguien puede explicarme por qué sucede esto? Parece violar mi comprensión del polimorfismo en OOP, pero tal vez me falta algo.

Aquí hay un código mucho más simple que muestra lo que estoy viendo:

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

Cuando esto se ejecuta, imprime " Contains 3? false " ;. Parece que se llama a la función igual (Objeto), aunque hay otra que funcionaría. Por el contrario, si escribo iguales como este, el código funciona como se esperaba:

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

¿Por qué no está averiguando qué versión de la función llamar según el tipo de parámetro?

¿Fue útil?

Solución

Estás mezclando " anulando " y " sobrecargando " ;.

Anulación: agregar una definición de reemplazo de un método existente para fines de polimorfismo. El método debe tener la misma firma. La firma consta del nombre y los tipos de argumento. Los métodos anulados se seleccionan en tiempo de ejecución en función del tipo de tiempo de ejecución del objeto de destino.

Sobrecarga: agregar un método con el mismo nombre pero con una firma diferente. Los métodos sobrecargados se seleccionan en tiempo de compilación en función del tipo de tiempo de compilación del objeto de destino.

Otros consejos

equals (Object) está anulando un súper método; puede no anular un súper método sin usar exactamente la misma firma (Bueno, hay algunas excepciones como retorntypes covariantes y excepción).

Observe que el método al que está llamando está definido en el javadoc para ArrayList <E > como

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

en lugar de

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

Implementación de 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;
}

Utiliza el método equals definido en la superclase Object ya que el método equals no se anula en la implementación de ArrayList <=> >

Al anular Object es igual en java, también debe anular el método Object hashCode.

De todos modos, es posible que desee probar el siguiente código:

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.

La implementación de ArrayList del método contiene (Objeto) está destinada a utilizar el método Object.equals (Objeto) internamente, por lo que nunca sabrá sobre su sobrecarga del método de igualdad (MyClass). Solo se encontrará un método de anulación (con firma coincidente).

Ok, déjame reformular.

(1) Debido a que el compilador elimina toda la información relacionada con los genéricos (borrado, consulte aquí ), y (2) porque no puede anular un método sin la misma firma (igual (Objeto)), (3) durante el tiempo de ejecución, todos los objetos dentro de la Lista se tratan como Objetos y no como instancias de MyClass Por lo tanto, el método que se llama es igual a (Objeto) ya que este es el que ha sido sobrescrito por su clase.

Está suponiendo que el método contains() en List conoce el tipo de objeto en tiempo de ejecución, lo cual es incorrecto.

Debido a la eliminación, List<MyClass> se convierte en un Object normal en tiempo de ejecución, por lo que el método equals() ve su parámetro como un MyClass, invocando el objeto <=> en lugar del que definió para <= > en su ejecución.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top