Lors du remplacement d'égaux en Java, pourquoi ne pas utiliser un paramètre autre que Object?

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

  •  08-07-2019
  •  | 
  •  

Question

J'ai eu récemment un comportement intéressant. Il semble que si je substitue .equals () à prendre un paramètre autre que Object, il ne sera pas appelé. Quelqu'un peut-il m'expliquer pourquoi cela se produit? Cela semble aller à l’encontre de ma compréhension du polymorphisme en POO, mais peut-être que je manque quelque chose.

Voici un code beaucoup plus simple qui montre ce que je vois:

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

Une fois exécuté, il affiche & "; Contains 3? false &". Il semble que la fonction equals (Object) soit appelée, même s’il en existe une autre qui fonctionnerait. Par contre, si j’écris comme égal, le code fonctionne comme prévu:

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

Pourquoi ne détermine-t-il pas la version de la fonction à appeler en fonction du type du paramètre?

Était-ce utile?

La solution

Vous confondez & "remplacer" & "; et " surcharge ".

Remplacement - ajout d’une définition de remplacement d’une méthode existante à des fins de polymorphisme. La méthode doit avoir la même signature. La signature est composée du nom et des types d'argument. Les méthodes remplacées sont sélectionnées au moment de l'exécution en fonction du type d'exécution de l'objet cible.

Surcharge - ajout d’une méthode portant le même nom mais une signature différente. Les méthodes surchargées sont sélectionnées lors de la compilation en fonction du type de compilation de l’objet cible.

Autres conseils

equals (Object) substitue une super méthode; vous ne pouvez pas redéfinir une super méthode sans utiliser la même signature (dans ce cas, il existe des exceptions telles que les types de retour covariants et les exceptions).

Notez que la méthode que vous appelez est définie dans le javadoc pour ArrayList <E > comme

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

au lieu de

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

Implémentation 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;
}

Il utilise la méthode equals définie dans la superclasse Object car la méthode equals n'est pas substituée dans ArrayList <=> > ;'s.

Lors de la substitution d'objet égal à java, vous devez également redéfinir la méthode Object hashCode.

Quoi qu’il en soit, vous voudrez peut-être essayer le code suivant:

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’implémentation ArrayList de la méthode includes (Object) est obligée d’utiliser la méthode Object.equals (Object) en interne, afin qu’elle ne sache jamais si vous surchargez la méthode equals (MyClass). Seule une méthode de substitution (avec la signature correspondante) sera trouvée.

Ok, laissez-moi reformuler.

(1) Le compilateur éliminant toutes les informations relatives aux génériques (effacement, voir here ), et (2) car vous ne pouvez pas remplacer une méthode sans la signature exacte (égal à (Object)), (3) pendant l'exécution, tous les objets de la liste sont traités comme des objets et non comme des instances. de MyClass. Par conséquent, la méthode appelée est égale à (Object) puisqu'il s'agit de celle qui a été écrasée par votre classe.

Vous supposez que la méthode contains() de List connaît le type de l'objet au moment de l'exécution, ce qui est incorrect.

En raison de l'effacement, List<MyClass> devient simplement un Object normal au moment de l'exécution, de sorte que la méthode equals() voit son paramètre comme un MyClass, invoquant ainsi le <=> de Object au lieu de celui que vous avez défini pour <= > dans son exécution.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top