IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

avec Java Discussion :

Problème avec le polymorphisme


Sujet :

avec Java

  1. #1
    Membre confirmé
    Profil pro
    Developper
    Inscrit en
    Juin 2008
    Messages
    55
    Détails du profil
    Informations personnelles :
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Developper

    Informations forums :
    Inscription : Juin 2008
    Messages : 55
    Par défaut Problème avec le polymorphisme
    Bonjour,

    je croyais avoir compris le polymorphisme (enfin pas toutes les arcanes, mais l'idée générale quoi), mais l'exemple suivant me pose problème :
    2 classes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class A
    {
    	int f(A a)
    	{
    		return 1;
    	}
    }
     
    class B extends A
    {
    	int f(A a)
    	{
    		return 2;
    	}
     
    	int f(B b)
    	{
    		return 3;
    	}
    }
    le main :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
        public static void main(String[] args) {
            A a = new A();
            A ab = new B();
            B b = new B();
     
            // Partie a
            System.out.println( a.getClass() );
            System.out.println( a.f(a) );
            System.out.println( a.f(ab) );
            System.out.println( a.f(b) );
            System.out.println("");
            // Partie ab
            System.out.println( ab.getClass() );
            System.out.println( ab.f(a) );
            System.out.println( ab.f(ab) );
            System.out.println( ab.f(b) );
            System.out.println("");
            // Partie b
            System.out.println( ab.getClass() );
            System.out.println( b.f(a) );
            System.out.println( b.f(ab) );
            System.out.println( b.f(b) );
            System.out.println("");
     
        }
    et le résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    class javaapplication1.A
    1
    1
    1
     
    class javaapplication1.B
    2
    2
    2
     
    class javaapplication1.B
    2
    2
    3
    Le 1er lot, OK, a est une classe A donc f renvoie 1. Mais sur le second, j'ai du mal, notamment : System.out.println( ab.f(ab) )
    comme on a un 2, ça veux dire qu'on considère que la méthode f est celle de la classe B, donc ab. est un objet B, mais comme on a 2 ça veux dire qu'on prend la méthode int f(A a) donc ça veux dire que ab passé en paramètre est une classe A...
    j'aurais compris qu'on ai 1 : ab est A ou qu'on ait 3 : ab est B... mais qu'on ait 2, là, je comprend pas. Je sais que le polymorphisme c'est justement que ab est B et est A... mais que dans la même expression ab soit A et B, j'ai du mal

    Quelqu'un aurait la gentillesse de m'expliquer ?

    Merci

    harf
    geff

  2. #2
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,



    Déjà tu as deux concepts différents :
    • Le polymorphisme par dérivation (ou héritage), que l'on appelle généralement redéfinition (ou overrides en anglais).

      Cela consiste à créer dans une classe fille une méthode avec la même signature, qui contiendrait un code différent. Dans ton exemple la méthode f(A) de la classe B est une redéfinition de la méthode f(A) de la classe A. Ces deux méthodes ont un lien de parenté entre elles (c'est très important on le verra plus bas).

    • Le polymorphisme ad hoc, que l'on appelle généralement surcharge (ou overload en anglais).

      Cela consiste à créer dans une même classe une méthode avec le même nom mais des paramètres différents (en type ou en nombre). En fait malgré leurs noms identiques, il faut considérer les méthodes comme étant totalement différentes : il n'y a aucun lien entre elle...





    Ensuite il faut comprendre que par défaut en Java les méthodes sont virtuelle. C'est à dire que la méthode qui sera réellement exécuté dépendra du type réel à l'exécution, selon la méthode choisi à la compilation.


    Cela se fait donc en deux étapes.

    C'est à la compilation que le choix de la méthode a appellé est déterminé, selon le type déclaré de la variable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
            // Partie a
            System.out.println( a.getClass() );	// a est déclaré en type A, qui ne possède qu'une méthode f(A)
            System.out.println( a.f(a) );		// => A.f(A)
            System.out.println( a.f(ab) );		// => A.f(A)
            System.out.println( a.f(b) );		// => A.f(A)
            System.out.println("");
            // Partie ab
            System.out.println( ab.getClass() );	// ab est déclaré en type A, qui ne possède qu'une méthode f(A)
            System.out.println( ab.f(a) );		// => A.f(A)
            System.out.println( ab.f(ab) );		// => A.f(A)
            System.out.println( ab.f(b) );		// => A.f(A)
            System.out.println("");
            // Partie b
            System.out.println( ab.getClass() );	// b est déclaré en type B, qui possède une méthode f(A) et f(B)
            System.out.println( b.f(a) );		// => B.f(A)
            System.out.println( b.f(ab) );		// => B.f(A) car ab est déclaré comme un type A
            System.out.println( b.f(b) );		// => B.f(B)
            System.out.println("");
    Cela correspond peut être un peu plus à ce que tu attendais. Mais il ne s'agit ici que de la compilation. A l'exécution la notion de méthode virtuelle rentre en jeu : pour chaque méthode virtuelle, la machine virtuelle va vérifier le type réel de l'instance pour éventuellement appeller une redéfinition de méthode (c'est là que rentre en jeu le lien de parenté) :

    En fait avant d'exécuter une méthode, la JVM va vérifier le type réel de l'objet afin de vérifier s'il existe une redéfinition de la méthode pour appeller cette méthode là plutôt que la méthode parente.

    Donc lorsque tu exécutes la méthode A.f(A) sur un objet de type réel B, la JVM va voir que la méthode est redéfini et va donc exécuter le code redéfini.

    Sur les références "a" et "b" il n'y a pas de modification car le type déclaré est le même que le type réel, mais sur la référence "ab" tu as une différence donc la JVM joue son rôle (elle recherche la redéfinition de méthode) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ab.f(a); // Appel de A.f(A) sur "ab"
    // => La JVM vérifie le type réel de "ab" qui est "B"
    // => Le type B a redéfini la méthode f(A)
    // => La JVM exécute le code de la méthode B.f(A)


    Je ne sais pas si c'est clair ??

    a++

  3. #3
    Membre confirmé
    Profil pro
    Developper
    Inscrit en
    Juin 2008
    Messages
    55
    Détails du profil
    Informations personnelles :
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Developper

    Informations forums :
    Inscription : Juin 2008
    Messages : 55
    Par défaut
    Merci beaucoup pour ta réponse argumentée, mais je bute toujours sur ab.f(ab) qui renvoie 2

    OK la JVM trouve que ab est bien de type B (jusque là, je comprend)
    or dans la classe B, j'ai "int f(B b) { return 3; }" donc ab.f(ab) revient, pour moi à "objet_de_classe_B.f(objet_de_classe_B) donc 3... Or, en fait c'est égale à 2, donc ça se comporte comme objet_de_classe_B.f(objet_de_classe_A)

    j'en profite pour une nouvelle question parallèle : c'est quoi la 3ème forme de polymorphisme ?

    merci encore

  4. #4
    Membre éclairé
    Homme Profil pro
    Développeur full-stack
    Inscrit en
    Mai 2002
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur full-stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 46
    Par défaut
    Le cas difficile:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A ab = new B();
    System.out.println( ab.f(ab) );
    A l'exécution, il va falloir trouver la méthode à appeler:

    -d'abord quelle classe ?
    dans l'expression "ab." on détermine le type de l'objet ab: class B

    - ensuite quelle méthode ?
    dans l'expression "ab.f(ab)"
    pour ça il faut déterminer le type de la référence passée en paramètre (et non le type de l'objet) càd A
    (ceci est vrai en java mais pas dans tous les langages objet)

    -> donc on apelle "B.f(A a)" (return 2)

    pas de problème avec type d'objet / type d'une référence ?

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur full-stack
    Inscrit en
    Mai 2002
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur full-stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 46
    Par défaut
    euuh, je viens de tester, je suis plus trop sûr de ce que j'avance
    ça m'intrigue, je continu de tester

  6. #6
    Membre éclairé
    Homme Profil pro
    Développeur full-stack
    Inscrit en
    Mai 2002
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur full-stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 46
    Par défaut
    ok, j'ai oublié une étape:

    au début de la recherche de méthode, il faut restreindre la liste des méthodes possibles en gardant que celles qui sont visibles.

    donc dans notre cas:
    dans l'expression "ab." la référence ab est de type A donc ne seront visible que les méthodes de A (la méthode f(B) ne pourra pas être exécutée)

    c'est pas terrible comme explication, mais c'est pas tous les jour qu'on voit des cas dans ce genre ^^

  7. #7
    Membre confirmé
    Profil pro
    Developper
    Inscrit en
    Juin 2008
    Messages
    55
    Détails du profil
    Informations personnelles :
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Developper

    Informations forums :
    Inscription : Juin 2008
    Messages : 55
    Par défaut
    C'est vrai que ce cas est vraiment tordu... merci pour votre aide.
    Je me suis focalisé sur ab.f(ab), mais en fait ab.f(b) est encore plus inattendu (enfin à mon niveau) : ab.f(b) renvoie 2 donc on a bien utilisé la méthode de la classe B, mais en considérant que b est un objet A... alors que b est un objet B (et donc A par polymorphisme, mais B d'abord,) donc pourquoi ne pas avoir utilisé la méthode f(b) ?

    Donc si je comprend bien ta réponse, avtonio, c'est que comme la "référence ab" est de type A, on ne peux voir que les méthodes hérités de A dans la classe de "l'objet ab" qui est la classe B. C'est ça ?

  8. #8
    Membre éclairé
    Homme Profil pro
    Développeur full-stack
    Inscrit en
    Mai 2002
    Messages
    46
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur full-stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 46
    Par défaut
    Donc si je comprend bien ta réponse, avtonio, c'est que comme la "référence ab" est de type A, on ne peux voir que les méthodes hérités de A dans la classe de "l'objet ab" qui est la classe B. C'est ça ?
    oui, avec une précision: "...comme la "référence ab" est de type A..." dans l'expression "ab."

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 18
    Par défaut
    Pour savoir par quel méthode il passe exactement tu devrait mettre 4 valeurs différentes.

    Mais si je ne me trompe pas :

    A ab = new B();
    System.out.println( ab.f(ab) );

    comme ab est créer en tant que A, il va executer les méthodes de la classe A. Mais le type de A est B car tu a fais un new B().
    D'où ton résultat.

  10. #10
    Membre confirmé
    Profil pro
    Developper
    Inscrit en
    Juin 2008
    Messages
    55
    Détails du profil
    Informations personnelles :
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Developper

    Informations forums :
    Inscription : Juin 2008
    Messages : 55
    Par défaut
    Ok ben merci à tous, sans dire que je suis à l'aise avec la chose, j'ai compris l'idée générale

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 18
    Par défaut
    Ce qu'il faut retenir la dessus c'est le cas suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class A
    {
    }
     
    class B extends A
    {
    }
     
    class C extends A
    {
    }
    l'intéret est de pouvoir stocker des objets B et C dans un vector<A>, afin de les transporters et de leur faire subir le même traitement.
    Mais apres 4 ans de développement dans le monde pro j'ai toujours vu ca comme cela :

    B b = new B();
    C c = new C();

    List<A> liste = new Vector<A>;
    liste.add(b);
    liste.add(c);

  12. #12
    Futur Membre du Club
    Inscrit en
    Avril 2004
    Messages
    3
    Détails du profil
    Informations forums :
    Inscription : Avril 2004
    Messages : 3
    Par défaut
    La question date un peu mais les réponses sont vagues.
    Une tentative de réponse simple et claire:

    ab est de type A.
    Il ne connait qu'une seule méthode: A.f(A) retournant 1.
    ab est une instance de B, il redéfinit donc son unique méthode A.f(A) pour retourner 2.
    ab accepte un type B en paramètre de cette méthode puisque B extend A.

  13. #13
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    Sauf que ça explique pas à la personne qui a pas compris,
    pourquoi c'est pas la méthode B.f(B) qui est appelée, puisque ab est une instance de B ?

    Réponse : parce que cette méthode n'existe pas dans la classe A et n'est donc pas une redéfinition d'une méthode existante dans A. ab étant de type déclaré A, seules les méthodes de A sont applicables. Ce qui n'empêche pas qu'elles puissent être redéfinies.

    Alors les explications claires, hein, c'est pas pour demain. Le polymorphisme en typage statique demande un peu d'habitude, c'est tout.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

Discussions similaires

  1. Problème avec le polymorphisme
    Par Vitis_Alba dans le forum C++
    Réponses: 14
    Dernier message: 15/01/2007, 16h41
  2. VC++ Direct3D8, problème avec LPD3DXFONT et LPD3DTEXTURE8
    Par Magus (Dave) dans le forum DirectX
    Réponses: 3
    Dernier message: 03/08/2002, 11h10
  3. Problème avec le type 'Corba::Any_out'
    Par Steven dans le forum CORBA
    Réponses: 2
    Dernier message: 14/07/2002, 18h48
  4. Problème avec la mémoire virtuelle
    Par Anonymous dans le forum CORBA
    Réponses: 13
    Dernier message: 16/04/2002, 16h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo