1
votes

Comment une instance polymorphe sélectionne-t-elle ses méthodes?

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);
    }
} 


1 commentaires

Vous auriez pu fournir un exemple un peu meilleur en supprimant tout le code inutile afin de le rendre minimal (voir exemple reproductible minimal ), 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.


4 Réponses :


1
votes

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.


8 commentaires

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.



0
votes

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.


1 commentaires

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")?



0
votes

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) .

Vous pouvez cependant appeler display () car il est défini dans A , mais ce sera toujours le C objet qui l'exécute.


0 commentaires