Quando l'override equivale a Java, perché non funziona per usare un parametro diverso da Object?

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

  •  08-07-2019
  •  | 
  •  

Domanda

Di recente ho riscontrato un comportamento interessante. Sembra che se eseguo l'override di .equals () per prendere un parametro diverso da Object, non viene chiamato. Qualcuno può spiegarmi perché questo sta accadendo? Sembra violare la mia comprensione del polimorfismo in OOP, ma forse mi manca qualcosa.

Ecco un codice molto più semplice che mostra ciò che vedo:

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

Quando viene eseguito, stampa " Contains 3? false " ;. Sembra che venga chiamata la funzione equals (Object), anche se ce n'è un'altra che funzionerebbe. Al contrario, se scrivo uguale a questo, il codice funziona come previsto:

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

Perché non è capire quale versione della funzione chiamare in base al tipo di parametro?

È stato utile?

Soluzione

Stai mescolando " sovrascrivendo " e " sovraccarico " ;.

Sostituzione - aggiunta di una definizione sostitutiva di un metodo esistente ai fini del polimorfismo. Il metodo deve avere la stessa firma. La firma è composta dal nome e dai tipi di argomento. I metodi sostituiti vengono selezionati in fase di esecuzione in base al tipo di runtime dell'oggetto di destinazione.

Sovraccarico: aggiunta di un metodo con lo stesso nome ma con una firma diversa. I metodi sovraccarichi vengono selezionati in fase di compilazione in base al tipo di ora di compilazione dell'oggetto target.

Altri suggerimenti

equals (Object) sta scavalcando un metodo super; puoi non sovrascrivere un super metodo senza usare la stessa identica firma (beh, ci sono alcune eccezioni come i tipi di ritorno covarianti e l'eccezione).

Si noti che il metodo che si sta chiamando è definito in javadoc per ArrayList <E > come

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

anziché

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

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

Utilizza il metodo equals definito nella superclasse Object poiché il metodo equals non viene sovrascritto nell'implementazione di ArrayList <=> >.

Quando si sovrascrive Object è uguale a Java, è necessario sovrascrivere anche il metodo Object hashCode.

Comunque potresti provare il seguente codice:

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.

L'implementazione di ArrayList del metodo contiene (Object) è destinata a utilizzare internamente il metodo Object.equals (Object), quindi non saprà mai il tuo sovraccarico del metodo equals (MyClass). Verrà trovato solo un metodo di sostituzione (con firma corrispondente).

Ok, fammi ripetere la frase.

(1) Poiché il compilatore elimina tutte le informazioni relative a Generics (cancellazione, vedere qui ) e (2) perché non è possibile sovrascrivere un metodo senza la stessa identica firma (uguale a (Oggetto)), (3) durante il runtime tutti gli oggetti all'interno dell'elenco vengono trattati come oggetti e non come istanze di MyClass. Quindi, il metodo che viene chiamato è uguale a (Object) poiché questo è quello che è stato sovrascritto dalla tua classe.

Stai presupponendo che il metodo contains() in List conosca il tipo di oggetto in fase di esecuzione, che non è corretto.

A causa della cancellazione, List<MyClass> diventa solo un normale Object in fase di esecuzione, quindi il metodo equals() vede il suo parametro come MyClass, invocando così l'oggetto <=> invece di quello definito per <= > nella sua esecuzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top