2011-04-14 2 views
14

Dans le code ci-dessous, comment les première et deuxième impressions impriment SubObj ?? Est-ce que le haut et le sous-point correspondent à la même sous-classe?Question sur la surcharge Java et la liaison dynamique

class Top { 
    public String f(Object o) {return "Top";} 
} 

class Sub extends Top { 
    public String f(String s) {return "Sub";} 
    public String f(Object o) {return "SubObj";} 
} 

public class Test { 
    public static void main(String[] args) { 
     Sub sub = new Sub(); 
     Top top = sub; 
     String str = "Something"; 
     Object obj = str; 


     System.out.println(top.f(obj)); 
     System.out.println(top.f(str)); 
     System.out.println(sub.f(obj)); 
     System.out.println(sub.f(str)); 
    } 
} 

Le code ci-dessus renvoie le résultat ci-dessous.

SubObj 
SubObj 
SubObj 
Sub 
+0

Maintenant, j'ai comment fonctionne la première ligne, mais comment se fait la deuxième ligne imprime SubObj, même si le dans L'appel put était top.f (str) où str est un type de String? – user482594

+0

J'ai posté une réponse, avez-vous regardé. Cela devrait résoudre vos doutes. Pour résumer penser à partir du point de "Type Check" pour l'argument passé. Veuillez accepter la réponse si vous le trouvez utile. –

Répondre

10

Puisque vous comprenez déjà le cas 1, 3 et 4, attaquons-nous à la boîte 2.

(S'il vous plaît note - Je ne suis pas un expert sur le fonctionnement interne de la machine virtuelle Java ou compilateurs, mais voici comment Je comprends que si quelqu'un lisant ceci est un expert de la JVM, n'hésitez pas à modifier cette réponse pour toute divergence que vous pourriez trouver.)

Une méthode dans une sous-classe qui a le même nom mais une signature différente est connue sous le nom de surcharge de méthode. . La surcharge de méthode utilise une liaison statique, ce qui signifie que la méthode appropriée sera forcée à être "choisie" (c'est-à-dire liée) au moment de la compilation. Le compilateur n'a aucune idée sur le type d'exécution (aka le type réel) de vos objets.Donc, quand vous écrivez:

      // Reference Type // Actual Type 
    Sub sub = new Sub(); // Sub    Sub 
    Top top = sub;  // Top    Sub 

le compilateur "sait" que le sommet est de type Top (alias le type de référence). Alors, quand vous écrivez plus tard:

System.out.println(top.f(str)); // Prints "subobj" 

le compilateur « voit » l'appel « top.f » comme se référant à la méthode f de classe. Il "sait" que str est de type String qui étend Object. Donc puisque 1) l'appel 'top.f' fait référence à la méthode f de Top class, 2) il n'y a pas de méthode f dans la classe Top qui prend un paramètre String, et 3) puisque str est une sous-classe d'Object, la méthode f de Top est le seul choix valide au moment de la compilation. Ainsi, le compilateur lance implicitement str vers son type parent, Object, de sorte qu'il peut être passé à la méthode f de Top. (Ceci est contraire à la liaison dynamique, où la résolution de type de la ligne de code ci-dessus serait différée jusqu'à l'exécution, pour être résolue par la JVM plutôt que par le compilateur.)

Puis à l'exécution, dans la ligne de code ci-dessus , top est downcast par la JVM à son type réel, sub. Cependant, l'argument str a été mis à jour par le compilateur pour taper Object. Alors maintenant, la JVM doit appeler une méthode f dans la classe sub qui prend un paramètre de type Object. Par conséquent, la ligne de code ci-dessus imprime "subobj" plutôt que "sub".

Pour un autre exemple similaire, s'il vous plaît voir: Java dynamic binding and method overriding

Mise à jour: Nous avons trouvé cet article détaillé sur le fonctionnement interne de la machine virtuelle Java:

http://www.artima.com/underthehood/invocationP.html

Je commentais votre code pour le rendre plus clair ce qui se passe:

class Top { 
    public String f(Object o) {return "Top";} 
} 

class Sub extends Top { 
    public String f(String s) {return "Sub";} // Overloading = No dynamic binding 
    public String f(Object o) {return "SubObj";} // Overriding = Dynamic binding 
} 

public class Test { 
    public static void main(String[] args) { 

            // Reference Type  Actual Type 
     Sub sub = new Sub();  // Sub    Sub 
     Top top = sub;   // Top    Sub 
     String str = "Something"; // String    String 
     Object obj = str;   // Object    String 

             // At Compile-Time:  At Run-Time: 
     // Dynamic Binding 
     System.out.println(top.f(obj)); // Top.f (Object) --> Sub.f (Object) 

     // Dynamic Binding 
     System.out.println(top.f(str)); // Top.f (Object) --> Sub.f (Object) 

     // Static Binding 
     System.out.println(sub.f(obj)); // Sub.f (Object)  Sub.f (Object) 

     // Static Binding 
     System.out.println(sub.f(str)); // Sub.f (String)  Sub.f (String) 
    } 
} 
1

Oui, ils pointent tous les deux vers la classe Sub. Le problème est que top sait seulement

f(Object o) 

et il peut appeler que cette signature. Mais sub connaît les deux signatures et doit sélectionner par type de paramètre.

6

En effet, tous les appels de méthode en Java sont virtual (par défaut).

Autrement dit, la résolution commence à l'objet réel (et non le type d'expression ) et « usine » la chaîne de transmission (par les objets réels de type) jusqu'à ce que le premier procédé de mise en correspondance est trouvée. Les méthodes non virtuelles commencent au type d'expression . (Marquage d'un procédé final rend non virtuelle.) Signature

Cependant, la méthode exacte est déterminée à la compilation (Java ne supporte pas le multi-Dispatch, une seule expédition varie seulement à Exécuter- temps basé sur l'objet récepteur) - ceci explique pourquoi Sub.f(String) résulte en "Sub", par exemple pendant que Top.f(String) "lie" à la méthode correspondante Top.f(Object) même si invoqué sur un sous-type de Top. (C'était la meilleure signature admissible déterminée à la compilation). La répartition virtuelle elle-même est la même.

Bonne codification.

2

Ceci a à voir avec le type apparent de l'objet. Au moment de la compilation, Java vérifie le type en fonction du type que vous déclarez être votre objet plutôt que du type spécifique que vous instanciez.

Vous avez un type Top avec une méthode f (Object). Alors, quand vous dites:

System.out.println(top.f(obj)); 

Le compilateur Java prend soin seulement que le haut de l'objet est de type supérieur et la seule méthode prend un objet en tant que paramètre. Au moment de l'exécution, il appelle ensuite la méthode f (Object) de l'objet instancié réel.

L'appel suivant est interprété de la même manière.

Les deux appels suivants sont interprétés comme prévu.

0

dans

Sub sub = new Sub(); 
Top top = sub; 

vous avez fait une instance de sous, puis vers le haut Casted vers le haut, ce qui en fait seulement savoir sur les méthodes qui existent en haut. la méthode qui existe dans le top est public String f(Object o) {return "Top";}

maintenant cette méthode est surchargé par sous il sera appelé lorsque vous faites une instance de sous et transtyper vers le haut.

une autre façon de mettre ce que vous avez obtenu est

sous type que le type apparent, mais haut comme le type réel, parce que vous avez attribué sous vers le haut. vous appellerez les méthodes du type apparent si elles surchargent le type réel, mais vous ne pourrez pas appeler toute méthode qui n'existe pas dans le type réel

1

En héritage, un objet de classe de base peut se référer à une instance de Classe dérivée.

Voici comment fonctionne Top top = sub;.

  1. Pour System.out.println(top.f(obj));:

    L'objet top tente d'utiliser la méthode f() de la classe Sub. Maintenant qu'il existe deux méthodes f() dans la classe Sub, la vérification de type est effectuée pour l'argument transmis. Puisque le type est Object la deuxième méthode f() de la classe Sub est invoquée.

  2. Pour System.out.println(top.f(str));:

    Vous pouvez interpréter les mêmes que (1) à savoir le type est String donc la première fonction f() s'invoquée.

  3. Pour System.out.println(sub.f(obj));:

    C'est simple que vous appelez la méthode elle-même classe de Sub. Maintenant qu'il y a deux méthodes surchargées dans la classe Sub, ici aussi la vérification du type est faite pour l'argument passé. Puisque l'argument passé est de type Object, la seconde méthode f() est invoquée.

  4. Pour System.out.println(sub.f(str));:

    similaires à 3., voici le type est passé String donc la première fonction f() de Sub classe s'invoquée.

Espérons que cela aide.

+0

Pour le second, la sortie réelle est 'SubObj' pas 'Sub' – user482594

Questions connexes