Liaison dynamic Java et substitution de méthode

Hier, j’ai eu un entretien téléphonique technique de deux heures (que j’ai réussi, woohoo!), Mais j’ai complètement étouffé la question suivante concernant la liaison dynamic en Java. Et il est doublement déconcertant parce que j’avais l’habitude d’enseigner ce concept aux étudiants de premier cycle quand j’étais assistant technique il y a quelques années, alors la perspective de leur avoir donné des informations erronées est un peu dérangeante …

Voici le problème qui m’a été donné:

/* 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( Ssortingng [] 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 deux instructions d’impression distinctes à partir de la méthode equals() t1.equals(t3) : à t1.equals(t3) et à t3.equals(t3) . Ce 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é en tant que type Test, donc la liaison dynamic doit appeler la forme surchargée de la méthode.

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

Ma question est alors, pourquoi? Comme je l’ai déjà mentionné, même si t1 est une référence de type Object (la liaison statique invoque donc la méthode equals() Object), la liaison dynamic doit invoquer la version la plus spécifique de la méthode basée sur le type instancié de la référence. Qu’est-ce que je rate?

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

Quelques discussions ici

Le fait qu’il s’agisse de la méthode des égaux n’est pas vraiment pertinent, sauf que c’est une erreur courante de le surcharger au lieu de le remplacer, ce que vous connaissez déjà en fonction de votre réponse au problème lors de l’interview.

Edit: Une bonne description ici aussi. Cet exemple montre plutôt 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 en fait dynamic, alors tout cas où l’appelant et le paramètre étaient une instance de Test entraînerait l’appel de la méthode surchargée. Donc, t3.equals (o1) serait le seul cas qui ne s’imprimerait pas.

La méthode equals de Test ne remplace pas la méthode equals de java.lang.Object . Regardez le type de paramètre! La classe Test est surchargée par une méthode qui accepte un Test .

Si la méthode equals est destinée à remplacer, elle devrait utiliser l’annotation @Override. Cela provoquerait une erreur de compilation pour signaler cette erreur courante.

Chose intéressante, 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 object n’appelle clairement pas la fonction Test.equals (Test).) En effet, groovy effectue un typage complètement dynamic. Ceci est particulièrement intéressant car il n’a pas de variables explicitement typées dynamicment. J’ai lu dans quelques endroits que cela est considéré comme nuisible, car les programmeurs s’attendent à ce que groovy fasse le truc java.

Java ne prend pas en charge la co-variance des parameters, mais uniquement dans les types de retour.

En d’autres termes, même si votre type de retour dans une méthode de substitution peut être un sous-type de ce qu’il était dans la substitution, cela n’est pas vrai pour les parameters.

Si votre paramètre pour equals in Object est Object, placer une valeur égale à autre chose dans une sous-classe sera une méthode surchargée et non pas 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 avec le processus d’entrevue d’emploi! Je serais ravi d’être interviewé dans une entreprise qui pose ces types de questions au lieu des questions habituelles sur les structures d’algo / données que j’enseigne à mes étudiants.

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 object Test, pas un object Object et ne remplace donc pas la méthode equals (). Cela signifie que vous ne l’avez réellement surchargé que pour faire quelque chose de spécial quand il a reçu un object Test tout en lui donnant des objects Object Object.equals (Object o). La recherche de ce code via un IDE devrait vous montrer deux méthodes equals () pour Test.

La méthode est surchargée au lieu d’être remplacée. Égaux prennent toujours un object comme paramètre.

Et bien, vous avez un object dans le java efficace de Bloch (que vous devriez posséder).

Certaines notes dans la liaison dynamic (DD) et la liaison statiquẹ̣̣ (SB) après la recherche pendant un certain temps:

1.Timing Exécuter : (Ref.1)

  • DB: au moment de l’exécution
  • SB: temps du compilateur

2.Used pour :

  • DB: passer outre
  • SB: surcharge (statique, privé, final) (Ref.2)

Référence:

  1. Exécuter le résolveur moyen que la méthode préfère utiliser
  2. Parce que ne peut pas surcharger la méthode avec le modificateur statique, privé ou final
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

Si une autre méthode est ajoutée et remplace au lieu de la surcharger, elle expliquera l’appel de liaison dynamic au moment de l’exécution.

/ * Quelle est la sortie du programme suivant? * /

 public class DynamicBinding { public boolean equals(Test other) { System.out.println("Inside of Test.equals"); return false; } @Override public boolean equals(Object other) { System.out.println("Inside @override: this is dynamic binding"); return false; } public static void main(Ssortingng[] 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 trouvé un article intéressant sur la liaison dynamic vs statique. Il est livré avec un morceau de code pour simuler la liaison dynamic. Cela a rendu mon code plus lisible.

https://sites.google.com/site/jeffhartkopf/covariance

Voir aussi cette question SO, étroitement liée: la substitution de la méthode JAVA à la méthode quirk

La réponse à la question “pourquoi?” C’est comme ça que le langage Java est défini.

Pour citer l’article de Wikipedia sur Covariance et Contravariance :

La covariance du type de retour est implémentée dans la version J2SE 5.0 du langage de programmation Java. Les types de parameters doivent être exactement les mêmes (invariant) pour la substitution de méthode, sinon la méthode est surchargée par une définition parallèle.

Les autres langues sont différentes.

Il est très clair qu’il n’ya pas de concept de dépassement ici. C’est la surcharge de méthode. la méthode Object() de la classe Object prend le paramètre de référence de type Object et cette méthode equal() prend le paramètre de référence de type Test.

Je vais essayer d’expliquer cela à travers deux exemples qui sont les versions étendues de certains des exemples que j’ai trouvés en ligne.

 public class Test { public boolean equals(Test other) { System.out.println("Inside of Test.equals"); return false; } @Override public boolean equals(Object other) { System.out.println("Inside of Test.equals ot type Object"); return false; } public static void main(Ssortingng[] 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 o1.equals(t2); System.out.println("\n" + count++); // prints 1 o1.equals(t3); System.out.println("\n" + count++);// prints 2 t1.equals(t2); System.out.println("\n" + count++);// prints 3 t1.equals(t3); System.out.println("\n" + count++);// prints 4 t3.equals(o1); System.out.println("\n" + count++);// prints 5 t3.equals(t3); System.out.println("\n" + count++);// prints 6 t3.equals(t2); } } 

Ici, pour les lignes avec des valeurs de comptage 0, 1, 2 et 3; nous avons référence à Object pour o1 et t1 sur la méthode equals() . Ainsi, au moment de la compilation, la méthode equals() du fichier Object.class sera liée.

Cependant, même si la référence de t1 est Object , elle a une initialisation de la classe Test .
Object t1 = new Test(); .
Par conséquent, au moment de l’exécution, il appelle le public boolean equals(Object other) qui est un

méthode surchargée

. entrer la description de l'image ici

Maintenant, pour les valeurs de comptage comme 4 et 6, il est à nouveau évident que t3 qui a une référence et une initialisation de Test appelle la méthode equals() avec un paramètre comme référence d’object et est un

méthode surchargée

D’ACCORD!

Encore une fois, pour mieux comprendre la méthode que le compilateur appellera, il suffit de cliquer sur la méthode et Eclipse mettra en évidence les méthodes de types similaires qu’il pense pouvoir appeler au moment de la compilation. Si elle n’est pas appelée au moment de la compilation, ces méthodes sont un exemple de dépassement de méthode.

entrer la description de l'image ici