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 :

UpCasting non respecté avec une méthode surchargée


Sujet :

Langage Java

  1. #1
    Membre averti
    Inscrit en
    Mai 2009
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 15
    Par défaut UpCasting non respecté avec une méthode surchargée
    Soit une classe A :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public class A {
     
        public void afficher () {
            System.out.println ("A");
        }
     
    }
    et une classe fille B qui surcharge la méthode afficher :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public class B extends A {
     
        // surcharge
        public void afficher () {
            System.out.println ("B");
        }
    }
    L'exécution du code suivant montre que le cast de B vers A (upcasting) est ignoré !?

    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
    public class TestUpcasting {
       
        public static void main(String[] args) {
            
            A objetA = new A();
            objetA.afficher ();
            // => affiche 'A' (pas de surprise)
            
            B objetB = new B();
            objetB.afficher ();
            // => affiche 'B' (pas de surprise là non plus)
            
            ((A) objetB).afficher ();
            // => affiche 'B' au lieu de 'A' !?
    
        }
    
    }
    1/ Pourquoi la méthode B.afficher() est-elle appelée au lieu de la méthode A.afficher() ?

    2/ Comment transformer un objet B en objet A ?

  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,

    Citation Envoyé par Nicolas Dansel Voir le message
    1/ Pourquoi la méthode B.afficher() est-elle appelée au lieu de la méthode A.afficher() ?
    Le compilateur génère bien dans le code un appel à la méthode A.afficher(), mais à l'exécution le type réel de l'objet est vérifié pour déterminer la méthode à appeler : B.afficher() dans ce cas.

    Si tu viens du C++ dis-toi qu'en Java toutes les méthodes sont virtuelle par défaut.

    Citation Envoyé par Nicolas Dansel Voir le message
    2/ Comment transformer un objet B en objet A ?
    Humm... C'est à dire ?

    a++

  3. #3
    Membre averti
    Inscrit en
    Mai 2009
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 15
    Par défaut
    Merci pour votre réponse rapide.

    Q1/ Il y a donc un écart entre le code tel qu'on le lit dans l'éditeur et le code réellement exécuté. Je m'en doutais :-) Maintenant, j'en suis sûr.

    Q2/ Disons que j'ai créé un objetB par erreur et que je veux maintenant le déclasser en pur objetA. Comment faire ?

    Le code suivant montre une tentative non concluante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
            A objetAbis = new A();
            objetAbis = objetB;
            objetAbis.afficher();
            // => affiche encore 'B' au lieu de 'A' ?!

  4. #4
    Membre averti
    Inscrit en
    Mai 2009
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 15
    Par défaut Remarque sur une doc Java
    Page suivante :

    http://fr.wikibooks.org/wiki/Program.../Polymorphisme

    Seconde section : Polymorphisme d'héritage (par redéfinition de méthodes dans une sous-classe)

    La dernière remarque me semble fausse au vu de ce que nous savons maintenant.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    public static void main(String[] args) {
      B ob1= new B(10,20);
      ((A)ob1).getter(); // Par type casting, appel de methode getter de la classe A
     }
    La méthode qui va s'exécuter sera en fait B.getter !

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    exact, le code mentionné dans le lien que tu donne est erronée. Ce sera le getter() de b qui sera appelé puisque c'est le type de B qui est utilisé.

    Selon les principe d'encapsulation en java, ce n'est pas a toi de choisir si tu va appeler le getter de A ou de B. Si B a redéfini getter() c'est qu'il en a une bonne raison. Si tu veux un objet A qui ne soit pas de type B, il faut faire un new A(), y a pas d'autre chose.

    Comme dit, si tu viens de c++, pratiquement tout les méthodes java sont l'équivalent de virtual en C++. (En java on parle de late binding, pas de virtual). Seules exception, les méthodes final (comme elle peuvent pas etre surchargées par définition, ok), les méthodes privées et les méthodes statique. Ainsi l'exemple que tu donne aurait été vrai dans 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
    13
    public class A {
        public static void afficher(){System.out.println("A");}
    }
    public class B extends A {
        public static void afficher(){System.out.println("B");}
    }
    A.affiche(); //A
    B.affiche(); //B
    B b = new B();
    b.affiche(); //B
    A a = b;
    a.affiche(); //A
    ((B)a).affiche(); //B

  6. #6
    Membre averti
    Inscrit en
    Mai 2009
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 15
    Par défaut DownCasting
    Merci pour la réponse, les exceptions et l'exemple !

    Je ne viens pas du C++ mais je suis attentif à certains comportements en Java qui échappent à ma compréhension 'basique' des concepts de la POO.

    Par exemple, je suis toujours étonné que l'on puisse faire un downcasting comme dans ton exemple sans se faire jeter par le compilateur.

    Je conçois que cela passe à l'exécution car a contient une référence vers un objet de type B.

    Mais pourquoi le compilateur autorise-t-il un tel cast ?!
    On n'est pas censé caster vers une sous-classe ?!

  7. #7
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    a est une instance de A, donc a peut aussi bien être de la class A que de la classe B, le compilateur n'en sais rien. Donc ce cast est tout a fait légitime. Là ou ca deviens tendancieux c'est qu'on utilise un instance (ici a typcasté sur type B) pour accéder à une méthode statique. Par contre, comme tout appel statique, le compilateur utilise le type déclaré dans le code. Ici, a cause du cast explicite, il utilise le type "B". Autant etre clair, c'est faisable (voir mon code) mais pas recommandé (on recommande de passer par le nom de classe pour les appels statique).

    ce code, a contrario, aurait échoué à l'exécution, car meme si le type casting ne sert à rien à l'exécution pour un appel statique, de part sa présence, le compilateur génère le code de typecasting, et sourtout la vérification
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    		A a = new A();
    		((B)a).affiche();//compile ok, référénce B.affiche(), mais crash à l'exécution avant l'appel à affiche (classCastException)

    pour référence, le compilateur laisse presque toujours faire le typecasting explicite (alors que pour les typecasting implicite, c'est uniquement vers le parent) sauf dans le cas où il sait clairement que le typecasting ne peux pas marcher:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public class C {
     
    }
    // main:
    		A a = new B(); // ok
    		B b = (B)a; // typecasting sera vérifié à l'exécution
    		C c = (C)a;//le compilateur sais que ce typecasting ne peux pas réussir à l'exécution, il refuse donc de compiler "Cannot cast from A to C"
    En effet, il est impossible vu sa hiérarchie, que C soit aussi un A.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    21
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Somme (Picardie)

    Informations forums :
    Inscription : Mai 2009
    Messages : 21
    Par défaut
    Citation Envoyé par Nicolas Dansel Voir le message
    Merci pour votre réponse rapide.

    Q1/ Il y a donc un écart entre le code tel qu'on le lit dans l'éditeur et le code réellement exécuté. Je m'en doutais :-) Maintenant, j'en suis sûr.

    Q2/ Disons que j'ai créé un objetB par erreur et que je veux maintenant le déclasser en pur objetA. Comment faire ?

    Le code suivant montre une tentative non concluante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
            A objetAbis = new A();
            objetAbis = objetB;
            objetAbis.afficher();
            // => affiche encore 'B' au lieu de 'A' ?!
    Implémente dans A un constructeur prenant en paramètre un objet de classe B, et instanciant l'objet A celon cet objet B, non?

  9. #9
    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
    Citation Envoyé par Nicolas Dansel Voir le message
    Mais pourquoi le compilateur autorise-t-il un tel cast ?!
    On n'est pas censé caster vers une sous-classe ?!
    Il y a deux types de cast :
    • L'upcasting qui est parfaitement sûr, car le compilateur peut vérifier la relation parent/enfant entre les deux types déclaré, par exemple :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      B b = ...;
      A a = (A) b;
      Comme B hérite de A, on peut upcasting une référence de B vers A sans que cela ne pose aucun problème puisque le compilateur pourra vérifier que le type B hérite du type A. D'ailleurs ici le cast peut être implicite :
      Ceci est particulièrement utilisé pour effectuer des traitements génériques, par exemple :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      public static void afficherNfois(A a, int count) {
          for (int i=0; i<count; i++) {
              a.afficher();
          }
      }
      On pourra utiliser cette méthode avec n'importe quel type A ou héritant de A, et ce sera à chaque fois la méthode afficher() la plus adapté qui sera utilisé selon le type réel de l'objet...


    • Le downcasting, qui consiste à descendre dans l'héritage vers un enfant :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      A a = ...;
      B b = (B) a;
      Ici contrairement à l'upcasting le compilateur ne peut pas vérifier la cohérence de l'ensemble : la référence "a" pourrait pointer vers un objet de type AA qui hériterait de A mais pas de B et dans ce cas là cela génèrera une ClassCastException à l'exécution.

      L'utilisation du downcasting dans le code n'est pas sécurisé, d'où l'obligation d'utilisé un cast explicite. On peut néanmoins sécurisé cela via des instanceof

      Note : toutefois le compilateur peut détecter des downcast totalement incohérent, par exemple lorsque deux types ne peuvent pas avoir de classe commune :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      Date date = new Date();
      String str = (String) date; // ERREUR de compil : type incompatible
      Dans beaucoup de cas, le downcasting peut être remplacé par l'utilisation des Generics (qui permettent un downcasting transparent et sécurisé), mais il peuvent également permettre d'exécuter des traitements spécifiques dans certains cas.



    a++

  10. #10
    Membre averti
    Inscrit en
    Mai 2009
    Messages
    15
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 15
    Par défaut
    Merci à toi grand chef pour cette brillante synthèse !
    Je vais donc m'orienter vers l'étude des "generics" pour approfondir ce sujet.
    Ciao

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 01/05/2007, 18h50
  2. Réponses: 3
    Dernier message: 22/02/2007, 20h02
  3. Problème avec une méthode virtuelle pure
    Par Burckel dans le forum C++
    Réponses: 4
    Dernier message: 05/12/2006, 13h00
  4. setTimeout avec une méthode privée
    Par Erakis dans le forum Général JavaScript
    Réponses: 12
    Dernier message: 29/06/2006, 10h47
  5. Non exécution d'une méthode repaint()
    Par Flophx dans le forum AWT/Swing
    Réponses: 7
    Dernier message: 05/05/2006, 18h04

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