Partage
  • Partager sur Facebook
  • Partager sur Twitter

Polymorphisme d'héritage en java

Sujet résolu
16 mai 2016 à 19:20:07

Bonsoir les amis,

je suis débutant en java et aujourd'hui je rencontre un problème sérieux sur le polymorphisme d'héritage.

Partons de cet exemple :

class A{
  void m(A a){
    System.out.println("m de A");
  }
  void n(A a){
    System.out.println("n de A");
  }
}


class B extends A{
  void m(A a){
    System.out.println("m de B");
  }
  void n(B b){
    System.out.println("n de B");
  }
  public static void main(String[] argv){
    A a = new B();
    B b = new B();
    a.m(b);
    a.n(b);
  }
}

Pour a.m(b) je comprends que c'est la méthode m() de B qui sera appelée (avec la résolution dynamique des liens) mais avec a.n(b) je ne comprends pas vraiment pourquoi c'est la fonction m() de A qui sera appelée.

Voici la réponse de ce code :

m de B

n de A

Merci d'avance

  • Partager sur Facebook
  • Partager sur Twitter
16 mai 2016 à 19:47:08

Salut,

Pour "a.n(b)", la méthode définie dans B n'a pas le même prototype que la méthode définie dans A (pas le même type de paramètre). Donc la méthode définie dans B ne va pas remplacer celle de A.
On peut considérer que dans la classe B il y a deux méthodes n avec des paramètres différents, la méthode "void n(A a)" qui est héritée de A, et la "void n(B a)" qui est définie directement dans B.

Maintenant pourquoi c'est "void n(A a)" qui est appelée et non "void n(B a)".
Il faut voir que le choix entre ces deux prototypes est fait à la compilation et non à l'exécution.
Le compilateur n'est pas super intelligent, il ne va pas remonter le code pour voir qu'en fait l'objet "a" est instance de B (il ne pourrait pas le faire dans tous les cas de toute façon). Tout ce qu'il voit c'est que l'objet "a" est déclaré en tant que A.

Quand on appelle "a.n(b)", il cherche donc un prototype de méthode qui correspondant dans la définition de "A". Comme dans la définition de A, "void n(B a)" n'existe pas, c'est "void n(A a)" qui est choisi.

Ensuite à l'exécution, comme la méthode "void n(A a)" n'est pas redéfinie dans B, c'est bien celle héritée de A qui est appelée.

Voici un autre exemple pour illustrer ou cette fois on redéfinie aussi "void n(A a)" dans B pour mieux comprendre :

public class B extends A {

        void n(A b) {
            System.out.println("n(A) de B");
        }

        void n(B b) {
            System.out.println("n(B) de B");
        }
}

public class A {


    void n(A a) {
        System.out.println("n(A) de A");
    }

    public static void main(String[] argv) {
        A a = new B();
        B b = new B();
        a.n(b);
        b.n(b);
    }

}


Affichera :

n(A) de B
n(B) de B

Dans le cas "a.n(b)", le compilateur décide qu'il faudra appeler le prototype "void n(A a)". Ensuite à l'exécution on appelle bien le "void n(A a)" de la classe B.

Dans le cas "b.n(b)" comme l'objet "b" est déclaré comme étant de type B, le compilateur en déduit que le prototype "void n(B b)" est celui qui correspond le mieux et c'est bien lui qui sera appelé à l'exécution.

-
Edité par macaque 16 mai 2016 à 20:04:40

  • Partager sur Facebook
  • Partager sur Twitter
16 mai 2016 à 20:17:40

macaque a écrit:

Salut,

Pour "a.n(b)", la méthode définie dans B n'a pas le même prototype que la méthode définie dans A (pas le même type de paramètre). Donc la méthode définie dans B ne va pas remplacer celle de A.
On peut considérer que dans la classe B il y a deux méthodes n avec des paramètres différents, la méthode "void n(A a)" qui est héritée de A, et la "void n(B a)" qui est héritée de B.

Maintenant pourquoi c'est "void n(A a)" qui est appelée et non "void n(B a)".
Il faut voir que le choix entre ces deux prototypes est fait à la compilation et non à l'exécution.
Le compilateur n'est pas super intelligent, il ne va pas remonter le code pour voir qu'en fait l'objet "a" est instance de B. Tout ce qu'il voit c'est qu'il est déclaré en tant que A.

Quand on appelle "a.n(b)", il cherche donc un prototype de méthode qui correspondant dans la définition de "A". Comme dans la définition de A, "void n(B a)" n'existe pas, c'est "void n(A a)" qui est choisi.

Ensuite à l'exécution, comme la méthode "void n(A a)" n'est pas redéfinie dans B, c'est bien celle héritée de A qui est appelée.

Voici un autre exemple pour illustrer ou cette fois on redéfinie aussi "void n(A a)" dans B pour mieux décomposer :

public class B extends A {

        void n(A b) {
            System.out.println("n(A) de B");
        }

        void n(B b) {
            System.out.println("n(B) de B");
        }
}

public class A {


    void n(A a) {
        System.out.println("n(A) de A");
    }

    public static void main(String[] argv) {
        A a = new B();
        B b = new B();
        a.n(b);
        b.n(b);
    }

}


Affichera :

n(A) de B
n(B) de B

-
Edité par macaque il y a une minute

Merci beaucoup pour votre réponse. En fait, je crois que je viens de comprendre beaucoup de choses. Donc on peut dire que le polymorphisme paramétrique est analysé pendant la compilation et le polymorphisme d'héritage pendant l'exécution si j'ai bien compris? Ou j'ai mal saisi? Merci encore  

-
Edité par Cedec 16 mai 2016 à 20:18:19

  • Partager sur Facebook
  • Partager sur Twitter
16 mai 2016 à 20:42:42

Oui je pense qu'on peut dire ça.

Le cas qu'on a étudié est un peu un mixte de des deux.

Pour le polymorphisme paramétrique pure, c'est plus simple de comparer "b.n(b)" et "b.n(a)" qui retourneront "n(B) de B" et "n(A) de B". Dans cet exemple là, à la compilation on cherche respectivement les prototype 'n(B b)" et "n(A a)" dans B puisque cette fois c'est le type exacte du paramètre que le compilateur ne peut inférer dans "b.n(a)".

En gros on peut voir le prototype de la méthode (son nom et les types de ces paramètres) comme la référence du code qui va être appelé.
A la compilation, on lie les appels du code compilé à ces références de méthode à appeler.
C'est ce qui permet le typage et de générer directement des erreurs si on essaie d'appeler des méthodes dont le prototype n'est définie nulle part.
A cette étape on n'a besoin de connaître que le prototype de la méthode, pas son implémentation : on peut très bien compiler du code qui appellerait un prototype de méthode définie dans une interface sans avoir jamais définie d'implémentation de cette interface nulle part.

Ensuite à l'exécution, on cherche la méthode correspondant à cette référence dans un objet précis qui lui existe et peut être instance d'une classe quelconque. Suivant qu'il soit instance de telle ou telle classe, l'implémentation de la méthode pourra donc être distinct et donc générer un résultat différent.

-
Edité par macaque 16 mai 2016 à 20:48:20

  • Partager sur Facebook
  • Partager sur Twitter
16 mai 2016 à 20:47:45

Test de faire un @Override avant tes fonctions dans la classe fille. Si la même existe dans la classe mère aucune erreur, sinon il te générera une erreur ;).

Faut pas oublier que ta classe B c'est comme si elle comprenais les méthodes de la classe de A ET de B ^^. Sachant que dans une même classe tu peux avoir deux méthodes du même nom avant des attribut différent.

Aucune idée de comment c'est fichu à la compilation etc ^^.

  • Partager sur Facebook
  • Partager sur Twitter
@autor VinYarD
16 mai 2016 à 21:22:56

Merci tout le monde,

maintenant je comprends bien le polymorphisme et c'est grâce à vous!

  • Partager sur Facebook
  • Partager sur Twitter
19 février 2020 à 8:35:45

bonjour les amis je ne parvient pas à savoir quand il y' a erreur de compilation et quand il y'a erreur d' exécution en cas de polymorphisme . Est ce que vous pouvez m'aider.
  • Partager sur Facebook
  • Partager sur Twitter
19 février 2020 à 9:24:46

Bonjour,

Déterrage

Citation des règles générales du forum :

Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.

Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre.
En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.

Au lieu de déterrer un sujet il est préférable :

  • soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
  • soit de créer un nouveau sujet décrivant votre propre contexte
  • ne pas répondre à un déterrage et le signaler à la modération

Je ferme ce sujet. En cas de désaccord, me contacter par MP.

  • Partager sur Facebook
  • Partager sur Twitter