J'ai du mal à comprendre le comportement de ce morceau de code. a est défini comme un A, c est défini comme un C. Puis, à la fin de la classe publique, a = c. Quand a appelle la méthode display (), il atteint la version C. Mais quand a appelle le f () il n'atteint que la version A, malgré le fait que les premiers arguments (byte et long) sont plus compatibles avec long que float.
C'est un exercice tiré d'un livre, mais les explications sont rares, ou inexistant.
class A{ public void display(){ System.out.println("I am an A "); } public void f(double x){ System.out.println("A.f(double = " + x + ") "); } } class C extends A{ public void display(){ System.out.println("I am a C ");} public void f(long q){ System.out.println("C.f(long = " + q + ") ");} } public class PolySurStack{ public static void main(String Args[]){ byte bb =1; long q = 4; float x = 5.f; System.out.println(" ** A **"); A a = new A(); a.display(); a.f(bb); a.f(x); System.out.println(); System.out.println(" ** C **"); C c = new C(); c.display(); c.f(bb); c.f(q); c.f(x); System.out.println(); a = c; a.display(); a.f(bb); a.f(q); a.f(x); } }
4 Réponses :
Lorsque vous appelez af (bb)
ou af (q)
ou af (x)
, les seules signatures de méthode que le compilateur peut choisir from sont ceux définis dans la classe A
(ou toute super classe de A
), puisque a
est une variable de référence de type A < / code>.
Par conséquent, seul public void f (double x)
est pris en compte. Pour que public void f (long q)
soit un candidat à la résolution de surcharge, vous devez lancer a
pour taper C
avant appelant f ()
, car seule la classe C
définit une méthode avec cette signature.
La chose importante à comprendre est que la résolution de surcharge de méthode a lieu au moment de la compilation. Seul le type de temps de compilation de la variable de référence pour laquelle vous appelez la méthode détermine quelles signatures de méthode sont candidates pour la résolution de surcharge de méthode, ainsi que quel candidat sera choisi.
Donc, en d'autres termes, cela se produit parce que display ()
est déterminable pendant la compilation et f ()
ne l'est pas?
@Gendarme à la fois display ()
et f (double x)
sont déterminables lors de la compilation (lorsque l'appel de méthode est pour une variable de type A). Cependant, dans le cas de display ()
, la substitution de méthode intervient pendant l'exécution - puisque C a une méthode display
avec la même signature , si a
contient une référence à une instance de la classe C
, la méthode display
de C
est exécutée au lieu de A
.
Eh bien, Eran, c'est mon malentendu, si, comme vous le dites: "les seules signatures de méthode parmi lesquelles le compilateur peut choisir sont celles définies dans la classe A (ou toute super classe de A)" pourquoi appelle-t-il display en C?
Parce que C
a écrasé display ()
alors qu'il était seulement surchargé f ()
, @Tchae.
@Gendarme, alors ce n'est pas vrai que a ne peut appeler que des méthodes en A? S'il fait référence à un objet C par exemple, il peut accéder aux méthodes en C?
@Tchae parce que A
et C
ont la même signature de méthode que la méthode display ()
. C'est la seule façon de passer outre. Le compilateur choisit une signature de méthode définie dans la classe A ( public void display ()
) mais en exécution, elle peut être remplacée par la méthode de classe C ayant la même signature.
C'est la même méthode, @Tchae. Il n'y a qu'un seul objet (l'objet C) et il a deux méthodes de f ()
, mais une seule méthode de display ()
.
Je suis désolé, @Gendarme, c'est comme le chinois pour moi. Je vais essayer de traiter ces informations. Cela peut prendre du temps ... Merci à tous pour vos réponses.
La méthode sélectionnée par le compilateur dépend du type déclaré et non du type d'exécution.
La première série qui se déclare comme variable A
ne peut invoquer que les méthodes A (quel que soit le type d'exécution instancié comme A
ne dérive que de Object
):
A a = new A(); // ... a = c; a.f(bb); a.f(q); a.f(x);
Pour la deuxième série, le compilateur lie les méthodes avec le paramètre le plus spécifique correspondant à l'invocation puisque C
est un A
et donc le le compilateur peut lier toutes les méthodes publiques de celles-ci ici:
C c = new C(); c.f(bb); c.f(q); c.f(x);
Mais dans le dernier morceau de code qui vous interroge probablement, a
fait référence à C
comme objet d'exécution mais à A
comme type déclaré:
A a = new A(); a.f(bb); a.f(x);
Donc, seules les méthodes définies dans A
peut être invoquée.
Mais alors pourquoi dans le "dernier morceau" a.display () (que vous avez jeté) invoque la méthode d'affichage en C (c'est-à-dire "Je suis un C")?
Je vais essayer de clarifier un peu la réponse de @eran pour que vous puissiez la comprendre.
Une sous-classe a toutes les méthodes de sa superclasse, et peut-être même d'autres en plus. Vous avez une variable de type A
, dans laquelle vous stockez un objet de type C
. C
a toutes les méthodes qui sont définies dans la classe A
, ainsi qu'une méthode supplémentaire qui est f (long q)
. A
ne connaît pas cette nouvelle méthode, donc puisque vous stockez l'objet dans une variable de A
, vous ne pouvez pas appeler f (long q) code >.
Vous pouvez cependant appeler display ()
car il est défini dans A
, mais ce sera toujours le C code> objet qui l'exécute.
Je viens de trouver ceci sur un autre forum:
Surcharge: (même nom de fonction mais signature différente)
Deux méthodes ou plus ayant le même nom avec des arguments différents dans la même classe sont appelées Overloading.
La surcharge est utilisée lorsque vous souhaitez étendre la fonctionnalité.
La surcharge est connue sous le nom de polymorphisme à la compilation
Remplacement: (même nom de fonction mais même signature)
Deux méthodes ou plus ayant le même nom de méthode et le même argument dans la classe parent et la classe enfant sont connues sous le nom de remplacement.
Le remplacement est utilisé lorsque vous souhaitez réutiliser la fonctionnalité existante.
Le remplacement est connu sous le nom de polymorphisme d'exécution
La réponse à ma question semble donc être que la résolution prioritaire (comme pour display ()) se produit au moment de l'exécution (ici après a = c) tandis que la résolution de surcharge (comme pour f ()) se produit au moment de la compilation, quand un est toujours un A.
Je pense.
J'ai également trouvé cette page: https://beginnersbook.com/ 2013/04 / polymorphisme-temps-de-compilation-runtime /
pour être clair et très pertinent sur ce sujet.
Vous auriez pu fournir un exemple un peu meilleur en supprimant tout le code inutile afin de le rendre minimal (voir exemple reproductible minimal a >), mais c'est néanmoins une question intéressante. Je ne connais pas la réponse, mais je crains que ce ne soit une question en double.