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 dynamique en Java. Et c'est doublement déroutant parce que j'avais l'habitude d'enseigner ce concept aux étudiants de premier cycle quand j'étais TA il y a quelques années, donc la perspective que je leur ai donné de la désinformation est un peu dérangeante ...
Voici le problème qui m'a été posé:
/* 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 deux instructions d'impression distinctes issues de la equals()
méthode remplacée : at t1.equals(t3)
et t3.equals(t3)
. Le dernier cas est assez évident, et avec le premier cas, même s'il t1
a une référence de type Object, il est instancié en tant que type Test, donc la liaison dynamique doit appeler la forme surchargée de la méthode.
Apparemment non. Mon enquêteur m'a encouragé à exécuter le programme moi-même, et voilà, il n'y avait qu'une seule sortie de la méthode remplacée: à la ligne t3.equals(t3)
.
Ma question est alors: pourquoi? Comme je l'ai déjà mentionné, même s'il t1
s'agit d'une référence de type Object (de sorte que la liaison statique appellerait la equals()
méthode Object ), la liaison dynamique devrait prendre soin d'invoquer la version la plus spécifique de la méthode en fonction du type instancié de la référence. Qu'est-ce que je rate?
la source
Réponses:
Java utilise la liaison statique pour les méthodes surchargées et la liaison dynamique pour les méthodes remplacées. Dans votre exemple, la méthode equals est surchargée (a un type de paramètre différent 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 égale n'est pas vraiment pertinent, à part que c'est une erreur courante de la surcharger au lieu de la remplacer, ce dont vous êtes déjà conscient en fonction de votre réponse au problème lors de l'entretien.
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 crois que si la liaison était réellement dynamique, alors tout cas où l'appelant et le paramètre étaient une instance de Test entraînerait l'appel de la méthode remplacée. Donc t3.equals (o1) serait le seul cas qui ne serait pas imprimé.
la source
La
equals
méthode deTest
ne remplace pas laequals
méthode dejava.lang.Object
. Regardez le type de paramètre! LaTest
classe est en surchargeequals
avec une méthode qui accepte unTest
.Si la
equals
méthode est destinée à remplacer, elle doit utiliser l'annotation @Override. Cela provoquerait une erreur de compilation pour signaler cette erreur courante.la source
Fait intéressant, dans le code Groovy (qui pourrait être compilé dans un fichier de classe), tous les appels sauf un exécuteraient l'instruction d'impression. (Celui qui compare un test à un objet n'appellera clairement pas la fonction Test.equals (Test).) C'est parce que groovy fait un typage complètement dynamique. Ceci est particulièrement intéressant car il ne contient aucune variable explicitement typée dynamiquement. J'ai lu à plusieurs endroits que cela était considéré comme dangereux, car les programmeurs s'attendent à ce que groovy fasse le truc java.
la source
Java ne prend pas en charge la co-variance dans les paramètres, uniquement dans les types de retour.
En d'autres termes, alors que votre type de retour dans une méthode de substitution peut être un sous-type de ce qu'il était dans le remplacement, ce n'est pas vrai pour les paramètres.
Si votre paramètre pour equals dans Object est Object, placer un equals avec n'importe quoi d'autre dans une sous-classe sera une méthode surchargée et non remplacé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'entretien d'embauche! J'aimerais être interviewé dans une entreprise qui pose ces types de questions au lieu des questions habituelles sur les algo / structures de données que j'enseigne à mes étudiants.
la source
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, pas un objet Object et ne remplace donc pas la méthode equals (). Cela signifie que vous ne l'avez en fait surchargé pour faire quelque chose de spécial que lorsqu'il lui est donné un objet Test tout en lui donnant des appels d'objet Object.equals (Object o). La recherche de ce code dans n'importe quel IDE devrait vous montrer deux méthodes equals () pour Test.
la source
La méthode est surchargée au lieu d'être surchargée. Equals prend toujours un objet comme paramètre.
btw, vous avez un élément à ce sujet dans le java efficace de Bloch (que vous devriez posséder).
la source
Quelques remarques dans Dynamic Binding (DD) et Static Binding̣̣̣ (SB) après une recherche un certain temps:
1. exécution de la synchronisation : (Ref.1)
2. utilisé pour :
Référence:
la source
Si une autre méthode est ajoutée qui remplace au lieu de surcharger, elle expliquera l'appel de liaison dynamique au moment de l'exécution.
/ * Quelle est la sortie du programme suivant? * /
la source
J'ai trouvé un article intéressant sur la liaison dynamique et statique. Il est livré avec un morceau de code pour simuler la liaison dynamique. Cela a rendu mon code plus lisible.
https://sites.google.com/site/jeffhartkopf/covariance
la source
La réponse à la question "pourquoi?" c'est ainsi que le langage Java est défini.
Pour citer l'article de Wikipédia sur la covariance et la contravariance :
Les autres langues sont différentes.
la source
Il est très clair qu'il n'y a pas de concept de priorité ici. C'est une surcharge de méthode. la
Object()
méthode de la classe Object prend le paramètre de référence de type Object et cetteequal()
méthode prend le paramètre de référence de type Test.la source
Je vais essayer d'expliquer cela à travers deux exemples qui sont les versions étendues de certains des exemples que j'ai rencontrés en ligne.
Ici, pour les lignes avec des valeurs de comptage 0, 1, 2 et 3; nous avons la référence d' Object pour o1 et t1 sur la
equals()
méthode. Ainsi, au moment de la compilation, laequals()
méthode du fichier Object.class sera délimitée.Cependant, même si la référence de t1 est Object , elle a l' 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.
Maintenant, pour les valeurs de comptage 4 et 6, il est à nouveau simple que t3 qui a la référence et l' initialisation de Test appelle la
equals()
méthode avec le paramètre comme références d'objet et est unD'ACCORD!
la source