88 votes

Liaison dynamique et remplacement de méthodes en Java

Hier, j'ai passé un entretien téléphonique technique de deux heures (que j'ai réussi, ouf !), mais j'ai complètement raté la question suivante concernant les liaisons dynamiques en Java. C'est d'autant plus curieux que j'avais l'habitude d'enseigner ce concept à des étudiants de premier cycle lorsque j'étais assistant technique il y a quelques années, alors la perspective que je leur ai donné des informations erronées est un peu troublante...

Voici le problème qui m'a été soumis :

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

  public static void main( String [] args ) {
    Object t1 = new Test();
    Object t2 = new Test();
    Test t3 = new Test();
    Object o1 = new Object();

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

J'ai affirmé que la sortie aurait dû être constituée de deux instructions d'impression distinctes à l'intérieur de la directive surchargée. equals() méthode : à t1.equals(t3) et t3.equals(t3) . Le dernier cas est assez évident, et avec le premier cas, même si t1 a une référence de type Object, il est instancié comme type Test, donc la liaison dynamique doit appeler la forme surchargée de la méthode.

Apparemment pas. Mon interlocuteur m'a encouragé à exécuter le programme moi-même, et voilà qu'il n'y avait qu'une seule sortie de la méthode surchargée : à la ligne t3.equals(t3) .

Ma question est donc la suivante : pourquoi ? Comme je l'ai déjà mentionné, même si t1 est une référence de type Object (la liaison statique invoquerait donc la fonction equals() ), la liaison dynamique devrait se chargent d'invoquer la version la plus spécifique de la méthode en fonction du type instancié de la référence. Que me manque-t-il ?

81voto

Robin Points 15032

Java utilise la liaison statique pour les méthodes surchargées et la liaison dynamique pour les méthodes surchargées. Dans votre exemple, la méthode equals est surchargée (elle possède un type de paramètre différent de celui d'Object.equals()), la méthode appelée est donc liée à la méthode référence au moment de la compilation.

Quelques discussions ici

Le fait qu'il s'agisse de la méthode equals n'est pas vraiment pertinent, si ce n'est que c'est une erreur courante de la surcharger au lieu de la surcharger, ce dont vous êtes déjà conscient d'après votre réponse au problème de l'entretien.

Modifier : Une bonne description ici également. Cet exemple montre un problème similaire lié au type de paramètre, mais causé par le même problème.

Je pense que si la liaison était réellement dynamique, tous les cas où l'appelant et le paramètre sont une instance de Test entraîneraient l'appel de la méthode surchargée. Ainsi, t3.equals(o1) serait le seul cas qui ne s'imprimerait pas.

25voto

erickson Points 127945

Le site equals méthode de Test ne remplace pas la equals méthode de java.lang.Object . Regardez le type de paramètre ! Le site Test surcharge la classe equals avec une méthode qui accepte un Test .

Si le equals est destinée à être remplacée, elle doit utiliser l'annotation @Override. Cela provoquerait une erreur de compilation pour signaler cette erreur courante.

6voto

Benson Points 10705

Il est intéressant de noter que dans le code Groovy (qui pourrait être compilé dans un fichier de classe), tous les appels sauf un exécuteraient l'instruction print. (Celui qui compare un Test à un Objet n'appelle clairement pas la fonction Test.equals(Test)). Ceci est dû au fait que groovy DOIT effectuer un typage entièrement dynamique. Ceci est particulièrement intéressant car il n'a pas de variables qui sont explicitement typées dynamiquement. J'ai lu à quelques endroits que cela est considéré comme nuisible, car les programmeurs s'attendent à ce que groovy fasse la chose java.

5voto

Uri Points 50687

Java ne prend pas en charge la covariance dans les paramètres, mais uniquement dans les types de retour.

En d'autres termes, si votre type de retour dans une méthode de remplacement peut être un sous-type de ce qu'il était dans la méthode de remplacement, ce n'est pas le cas pour les paramètres.

Si votre paramètre pour equals dans Object est Object, mettre un equals avec n'importe quoi d'autre dans une sous-classe sera une méthode surchargée et non surchargée. Par conséquent, la seule situation où cette méthode sera appelée est lorsque le type statique du paramètre est Test, comme dans le cas de T3.

Bonne chance pour l'entretien d'embauche ! J'adorerais passer un entretien dans une entreprise qui pose ce type de questions au lieu des habituelles questions sur les algo/structures de données que j'enseigne à mes étudiants.

4voto

P Arrayah Points 302

Je pense que la clé réside dans le fait que la méthode equals() n'est pas conforme à la norme : Elle prend un autre objet Test, et non un objet Object, et ne surcharge donc pas la méthode equals(). Cela signifie que vous ne l'avez en fait surchargée que pour faire quelque chose de spécial lorsqu'on lui donne l'objet Test alors que l'objet Object appelle Object.equals(Object o). En regardant ce code dans n'importe quel IDE, vous devriez voir deux méthodes equals() pour Test.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X