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

Langage Java Discussion :

Polymorphisme étonnant: Java ne privilégie pas les méthodes aux objets spécialisés?


Sujet :

Langage Java

  1. #1
    Membre éclairé

    Profil pro
    Inscrit en
    janvier 2007
    Messages
    592
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 592
    Points : 655
    Points
    655
    Par défaut Polymorphisme étonnant: Java ne privilégie pas les méthodes aux objets spécialisés?
    J'en ai perdu mon latin et suis très embarrassé par les implications d'une expérience que j'ai menée.

    Deux objets A et B ont une fonction toString() qui renvoie "A" et "B".
    B hérite de A.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    public class A
    {
       public String toString() {return("A");}
    }
     
    public class B extends A
    {
       public String toString() {return("B");}
    }
    Une classe C est munie de deux méthodes toString(X x) prenant ces classes en argument:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    public class C
    {
       public String toString(A a) {return("C voit A");}
     
       public String toString(B b) {return("C voit B");}
    }

    Voici ce que j'expérimente:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    C c = new C();
     
    A a1 = new A();
    B b1 = new B();
    System.out.printf("a1 = %s, b1 = %s\n", a1, b1);
    System.out.printf("c.toString(a1) = %s, c.toString(b1) = %s\n", c.toString(a1), c.toString(b1));
    Résultat (sur la console):
    a1 = A, b1 = B
    c.toString(a1) = C voit A, c.toString(b1) = C voit B

    C'est ce à quoi je m'attendais.


    Mais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    A a2 = new B();
    System.out.printf("a2 = %s\n", a2);
    System.out.printf("c.toString(a2) = %s\n", c.toString(a2));
    Produit:
    a2 = B
    c.toString(a2) = C voit A

    D'un côté, Java a bien remarqué que a2 était un objet de classe B, puisqu'il dit a2 = B,
    D'un autre côté, il ne choisit pas le prototype de toString(X x) dans C que j'attendais, car il prend celui pour A.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    B b3 = new B();
    A a3 = (A)b3;
    A a4 = b3;
    System.out.printf("a3 = %s, b3 = %s, a4 = %s\n", a3, b3, a4);
    System.out.printf("c.toString(a3) = %s, c.toString(b3) = %s, c.toString(a4) = %s\n", c.toString(a3), c.toString(b3), c.toString(a4));
    Ces autres essais mènent aux mêmes constatations:
    a3 = B, b3 = B, a4 = B
    c.toString(a3) = C voit A, c.toString(b3) = C voit B, c.toString(a4) = C voit A


    Et cela ne me convient pas du tout!
    Cela ne vous choque t-il pas?

  2. #2
    Expert éminent sénior
    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
    Points : 23 015
    Points
    23 015
    Billets dans le blog
    1
    Par défaut
    Salut,



    Ton problème vient du fait qu'il existe plusieurs types de polymorphisme qui ne fonctionnent pas de la même manière et qui n'utilisent pas les mêmes mécanismes...


    • Dans le premier cas, il s'agit de polymorphisme par sous-typage, plus généralement appelée redéfinition (ou overrides en anglais).

      Tu as une méthode (toString()) que tu redéfinis dans les sous classes (A et B).

      Lors de la compilation cela génèrera une instruction invokevirtual sur toString(), et lors de l'exécution la JVM recherchera la bonne méthode à appeler selon le type réel de la classe.


    • Dans le second cas, il s'agit de polymorphisme ad hoc, appelée surcharge de méthodes (ou overloading en anglais).

      Tu utilises plusieurs paramètres de types différents pour la même méthode, mais en réalité tu as plusieurs méthodes : toString(A) et toString(B).

      Ces deux méthodes sont différentes car elles ont une signature différente (le type des paramètres fait partie de la signature). C'est exactement comme si tu avais deux méthodes avec deux noms différents.

      Dans ce cas précis, le compilateur génerera encore une instruction invokevirtual, mais sur deux méthodes différentes (toString(A) ou toString(B)) selon le type déclaré des arguments passé en paramètres.

      Le choix de la méthode a appeler se fait lors de la compilation selon le code source...



    Je ne sais pas si c'est bien clair...


    a++

  3. #3
    Membre régulier Avatar de fatypunk
    Profil pro
    Inscrit en
    octobre 2007
    Messages
    71
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : Suisse

    Informations forums :
    Inscription : octobre 2007
    Messages : 71
    Points : 74
    Points
    74
    Par défaut
    Ce que dit adiGuba est parfaitement exact, dans ton cas quand tu fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A a2 = new B();
    System.out.printf("c.toString(a2) = %s\n", c.toString(a2));
    Tu passe a2 à c.toString, or a2 est déclaré comme étant une instance de A ! Donc c'est la méthode toString pour objet A qui est utilisée. Si tu désire appeler la méthode pour objet B, tu devrais faire un cast de a2 sur B :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A a2 = new B();
    System.out.printf("c.toString(a2) = %s\n", c.toString((B)a2));
    Ce comportement est logique et le contraire ne me plairait pas du tout, car il s'agirait d'une sorte de cast implicite... à mon avis très dangereux.
    Développeur Java SE, Java EE (EJB3)
    IDE : Netbeans 6.5 / Serveur d'application : Glassfish v2.1 / OS : Ubuntu 8.10 Intrepid Ibex et CentOS 5
    Historique : GWBasic, Turbo Pascal (beaucoup), Visual Basic, C (un peu), C++ (beaucoup), Assembleur (6800 et x86 / un peu), Java, Smalltalk (un peu), Lisp (un peu), Prolog (un peu), PHP, Ruby (un peu), et retour à Java (beaucoup).

    Pas de questions techniques par MP s'il vous plait !

  4. #4
    Membre éclairé

    Profil pro
    Inscrit en
    janvier 2007
    Messages
    592
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 592
    Points : 655
    Points
    655
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    A a2 = new B();
    System.out.printf("c.toString(a2) = %s\n", c.toString(a2));
    Cela pose quand-même un problème de cohérence, je trouve.


    Interprétation 1:
    On interprète A a2 = new B(); en disant qu'en adressant a2, on veut en fait adresser la classe B. Et que c'est pour cela que a2.toString() envoie sur B::toString(). Mais alors C.toSting(a2) devrait appeler C::toString(B b).

    Interprétation 2:
    On interprète A a2 = new B(); comme la volonté de caster B en A, et c'est pour cela que c.toString(a2) appelle C::toString(A a). Mais alors dans ce cas, pour être cohérent, a2.toString() devrait aussi appeler A::toString().

  5. #5
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2007
    Messages : 25 481
    Points : 48 794
    Points
    48 794
    Par défaut
    cela ne pose aucun problème de cohérence. Ce serait changer de méthode au vol qui poserais des problème de cohérence. Le programmeur n'aurait alors pas le controle sur la méthode qu'il appelle. Hors, il faut bien qu'il l'aie. Il y a deux chose à prendre en compte:

    1) le polymorphisme, ce sont des méthodes ayant le meme nom mais pas le meme comportement (on utiliser les paramètres de l'appel pour déterminer laquelle on utilise). Le choix de la méthode est résolu à la compilation! Parfois meme il est ambigu et nécessite une clarification:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public void test(String a, Object b){....}
    public void test(Object a, String b){....}
    .....
    test("123","456"); // erreur de compilation, on ne peut pas résoudre l'appel, ambiguité, il faut faire
    test((Object)"123","456");
    //ou
    test("123",(Object)"456");
    2) L'héritage, c'est la possibilité de créer une sous-classe qui peut etre entièrement substituée à son parent, de manière transparente pour le code qui l'utilise. La effectivement, pour les méthodes qui sont en latebinding, la résolution se fait à l'exécution. C'est en fait associé au processus de résolution de l'objet sur lequel tu fait l'appel. Alors que le choix des paramètre de l'appel est totalement indépendant de l'objet sur lequel tu fait l'appel et peut donc etre fait dès la compilation.

  6. #6
    Membre confirmé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    septembre 2006
    Messages
    572
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : septembre 2006
    Messages : 572
    Points : 624
    Points
    624
    Par défaut
    Est ce que l'annotation @override ne permet pas de justement descendre au toString de B, un peu comme les modifieurs "virtual" du c++ ? (c'est une vraie question, pas une suggestion :p)
    Venez partager vos expériences au sein d'un projet sur slicesofit, agile & amélioration continue

  7. #7
    Expert éminent sénior
    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
    Points : 23 015
    Points
    23 015
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Faiche Voir le message
    Est ce que l'annotation @override ne permet pas de justement descendre au toString de B, un peu comme les modifieurs "virtual" du c++ ? (c'est une vraie question, pas une suggestion :p)
    Je ne comprent pas ce que tu veux dire par là : en Java les méthodes sont virtual par défaut (sauf les méthodes static, private ou final).


    L'annotation @Override permet d'indiquer au compilateur que la méthode est une redéfinition d'une méthode de la classe parente. Le compilateur peut ainsi générer une erreur si ce n'est pas le cas (pratique pour déceler une signature incorrecte).

    A noter qu'avec Java 6 cela inclut également les implémentations de méthodes définies dans une interface

    Cf : Définition exacte de @Override


    a++

  8. #8
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : avril 2007
    Messages : 25 481
    Points : 48 794
    Points
    48 794
    Par défaut
    Citation Envoyé par Faiche Voir le message
    Est ce que l'annotation @override ne permet pas de justement descendre au toString de B, un peu comme les modifieurs "virtual" du c++ ? (c'est une vraie question, pas une suggestion :p)
    Comme déjà dit, çà n'a aucun sens de calculer l'appel de la méthode à l'appel plutot qu'à la compilation. D'abord çà poserais des problèmes de résolution d'ambiguités, chose résolvable à la compilation (via des cast explicite), mais pas à l'exécution.
    Ensuite, si on suis ta logique, çà poserais des problèmes du style:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public void x(String y){.....}
    public void x(Integer i){.......}
    .....
    Object a = .....;
    x(a);
    Le codeur pourrait argumenter que il "sait" que a est soit String soit Integer donc l'appel ne peux pas échouer. Mais si le compilateur accepte çà, il accepte tout et n'importe quoi, y compris par exemple une Collection, ce qui ne peux pas marcher. Bref on perd tout le typage et si c'est pour faire çà, autant faire du visual baisc ^^.

    Enfin, et pour te contredire, autant que je sache, c++ ne fait *pas* non plus la résolution des paramètres à l'exécution. De plus, en C++, la résolution du type d'objet sur lequel tu fait l'appel n'a pas lieu non plus, sauf si tu met explicitement "virtual", alors qu'en java, les méthodes non privée d'objet sont toujours virtuelles.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 29/03/2012, 13h08
  2. Je ne trouve pas les méthodes coté client
    Par Invité dans le forum Services Web
    Réponses: 1
    Dernier message: 24/02/2011, 19h15
  3. Réponses: 8
    Dernier message: 10/12/2009, 17h55
  4. Réponses: 6
    Dernier message: 24/11/2006, 13h21
  5. [JNI] Java ne trouve pas mes méthodes natives
    Par carotte31 dans le forum Entrée/Sortie
    Réponses: 5
    Dernier message: 14/06/2006, 22h47

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