Question

Hier, j’ai eu un entretien téléphonique technique de deux heures (que j’ai passé, woohoo!), mais j’ai complètement masqué la question suivante concernant la liaison dynamique en Java. Et c’est doublement déroutant parce que j’enseignais ce concept aux étudiants de premier cycle quand j’étais assistant enseignant il y a quelques années;

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( 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û consister en deux instructions d'impression distinctes de la méthode equals () remplacée: à t1.equals (t3) et t3 .equals (t3) . Le dernier cas est assez évident, et dans le premier cas, même si t1 a une référence de type Object, il est instancié en tant que type Test, la liaison dynamique doit donc appeler la forme remplacée de la méthode.

Apparemment non. Mon intervieweur m'a encouragé à exécuter le programme moi-même, et voici, il n'y avait qu'un seul résultat de 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 si t1 est une référence de type Object (la liaison statique invoque la méthode equals () de Object), la liaison dynamique doit veille à invoquer la version la plus spécifique de la méthode en fonction du type instancié de la référence. Qu'est-ce qui me manque?

Était-ce utile?

La solution

Java utilise une liaison statique pour les méthodes surchargées et une liaison dynamique pour les méthodes remplacées. Dans votre exemple, la méthode equals est surchargée (a un type param différent de Object.equals ()), de sorte que la méthode appelée est liée au type reference au moment de la compilation.

Quelques discussions ici

Le fait qu’il s’agisse de la méthode de l’égalité n’est pas vraiment pertinent, mais c’est une erreur courante de surcharger au lieu de la remplacer, ce que vous connaissez déjà d'après votre réponse au problème dans l'interview.

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, alors, dans tous les cas où l'appelant et le paramètre seraient une instance de Test, la méthode remplacée serait appelée. Donc, t3.equals (o1) serait le seul cas qui ne serait pas imprimé.

Autres conseils

La méthode égal à de Test ne remplace pas la méthode égal à de java.lang.Object . Regardez le type de paramètre! La classe Test surcharge est égal à avec une méthode qui accepte un Test .

Si la méthode égal à est destinée à être remplacée, elle devrait utiliser l'annotation @Override. Cela provoquerait une erreur de compilation pour signaler cette erreur courante.

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 print. (Celui qui compare un test à un objet n'appellera clairement pas la fonction Test.equals (Test).) Ceci est dû au fait que groovy FAIT un typage complètement dynamique. Ceci est particulièrement intéressant car il ne contient aucune variable explicitement typée de manière dynamique. J'ai lu dans plusieurs endroits que cela était considéré comme dangereux, car les programmeurs s'attendent à ce que groovy fasse la chose en java.

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

En d'autres termes, bien que votre type de retour dans une méthode de substitution puisse être un sous-type de ce qu'il était dans la substitution, cela n'est pas vrai pour les paramètres.

Si votre paramètre pour equals dans Object est Object, le fait de placer un équivalent avec tout le reste de la sous-classe sera une méthode surchargée et non surchargée. Par conséquent, la seule situation dans laquelle cette méthode sera appelée est lorsque le type statique du paramètre est Test, comme dans le cas de T3.

Bonne chance dans le processus d’entrevue d’emploi! J'aimerais être interviewé dans une entreprise qui pose ce type de questions plutôt que les questions habituelles relatives aux structures de données / algo 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 intègre un autre objet Test, pas un objet Object, et ne substitue donc pas la méthode equals (). Cela signifie que vous l'avez simplement surchargé pour faire quelque chose de spécial quand il reçoit un objet Test tout en lui donnant des objets Object, appelés Object.equals (Object o). Rechercher ce code dans n’importe quel environnement de développement intégré devrait vous montrer deux méthodes equals () pour Test.

La méthode est surchargée au lieu d’être remplacée. Equals prend toujours un objet en paramètre.

btw, vous avez un élément à ce sujet dans le java effectif de Bloch (que vous devriez posséder).

Certaines notes dans Liaison dynamique (DD) et Liaison statiqu??? (SB) après une recherche prolongée:

1.Exécution du minutage : (Ref.1)

  • DB: au moment de l'exécution
  • SB: heure du compilateur

2.Utilisé pour :

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

Référence:

  1. Exécuter le résolveur moyen quelle méthode préfère utiliser
  2. Parce que ne peut pas écraser la méthode avec le modificateur static, private 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 qui remplace au lieu de surcharger, elle expliquera l'appel de liaison dynamique au moment de l'exécution.

/ * Quel est le résultat 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(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 trouvé un article intéressant sur la liaison dynamique et statique. Il est livré avec un morceau de code permettant de simuler une liaison dynamique. Cela a rendu mon code plus lisible.

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

Voir aussi cette question SO, étroitement liée: Ignorer l'exagération de la méthode JAVA equals

La réponse à la question "pourquoi?" C’est ainsi que le langage Java est défini.

Pour citer le article de Wikipedia sur la covariance et la contrevenance :

  

La covariance de type de retour est implémentée   dans le langage de programmation Java   version J2SE 5.0. Les types de paramètres ont   être exactement le même (invariant) pour   méthode surchargée, sinon la   la méthode est surchargée avec un parallèle   définition à la place.

Les autres langues sont différentes.

Il est très clair qu'il n'y a pas de notion de priorité ici. C'est une méthode surchargée. la méthode Object () de la classe Object prend un paramètre de référence de type Object et cette méthode equal () prend un paramètre de référence de type Test.

Je vais essayer d'expliquer cela au moyen de deux exemples, qui sont les versions étendues de certains exemples que j'ai rencontré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(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
        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 de 0, 1, 2 et 3; nous avons référence de Objet pour o1 et t1 sur la méthode equals () . Ainsi, lors 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 Objet , elle contient une initialisation de la classe de test .
Objet 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 remplacée

. entrer la description de l'image ici

Maintenant, pour les valeurs de comptage de 4 et 6, il est à nouveau simple que t3 qui a référence et initialisation de Test appelle < code> equals () avec un paramètre comme référence à un objet et est un

  

méthode surchargée

OK!

  

Encore une fois, pour mieux comprendre quelle méthode le compilateur appellera, il suffit de   cliquez sur la méthode et Eclipse mettra en évidence les méthodes de même   types qu’elle pense appeler lors de la compilation. Si ça ne va pas   appelé au moment de la compilation, alors ces méthodes sont un exemple de méthode   surcharger.

 entrer la description de l'image ici

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