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

Java Discussion :

La généricité en Java et le mot clé super


Sujet :

Java

  1. #1
    Membre habitué
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2010
    Messages
    212
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2010
    Messages : 212
    Points : 184
    Points
    184
    Par défaut La généricité en Java et le mot clé super
    Salut,
    soit le code suivant:
    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
    public class Main {
     
        static void method(List<? super Number> list) {
            //
        }
     
        public static void main(String[] args) {
            List<? super Number> list = null;
            List<Object> objs = new ArrayList<>();
            method(objs); // OK
             list.add(12.7);// OK ??
            list.add(new Object()); // this doesn't compile ???
        }
     
    }
    la question est simple: pour quoi la dernière ligne ne se compile pas!!. je comprend bien pour quoi le compilateur n' a pas signaler une erreur dans la ligne suivante:

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    La déclaration List<? super Number> déclare que la liste est typée "sur un type bien précis", "inconnu", mais dont on sait au moins que ce type est un parent de "Number".

    L'important est cette partie "précis", qui n'est pas la meme chose que "n'importe quoi". Le ? dans la généricité veux dire "je ne sais pas", pas "n'importe quoi qui a l'air de matcher". Il est là pour restreindre les opérations possibles, pas pour les agrandir. Ainsi:

    Avec List<? super Number> je sais que je peux ajouter un Number, car c'est d'office compatible avec une liste typée sur quelque chose qui est parent de Number. Si c'est une List<Objet> en réalité, ça marche. Si c'est une List<Number> en réalité, ça marche. Et ce sont les seuls parents possibles.


    Avec List<? extends Number>, a contrario, je ne sais pas ce que je peux ajouter. Par contre, je sais que ce que je recevrais est au moins un Number. La liste réelle peut êtres List<Integer>, List<Long> ...

    Enfin avec List<?> je sais que c'est typé mais j'ignore tout de ce type. C'est très restrictif et limite vachement les opérations possibles.

    Enfin, avec List<Number> je sais que la liste est typée exactement avec Number, donc je peux y mettre des Long, des Integer,.... et je recevrais des choses compatible Number. C'est la déclaration la moins restrictive.


    Pour reprendre ton exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        public static void main(String[] args) {
            List<Number> liste1 = new ArrayList<Number>();
            List<? super Number> autreliste= liste1;
            autreliste.add(12.7);// un Float est une Number, c'est compatible avec autreliste
            autreliste.add(new Object()); // Et liste1 se retrouverais avec un "Object" alors qu'on a dit qu'elle contenait des Number....
        }
    Bref, tu imagine que List<? super Number> ne peux contenir que des parent de number (et du coup pas Float,Integer, ...) alors que c'est l'inverse. Elle ne peux contenir que des descendant de "? super Number", qui est inconnu mais dont on sait que c'est un parent de Number. Et un descendant de "Number" est donc bien un descendant de "? super Number". Donc on peux ajouter une Integer.

  3. #3
    Membre chevronné
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Points : 1 993
    Points
    1 993
    Par défaut
    Je te donne juste un exemple pour comprendre pourquoi on ne peut pas ajouter d'éléments dans une liste qui est déclarée de la forme ? super X (liste qui contient X ou ses super classes)

    Imagines le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    		List<? super Long> myList;
    		if (myCondition){
    		     myList = new ArrayList<Object>();
    		} else {
    		     myList = new ArrayList<Number>();
    		}
    Est-ce que tu peux me dire ce qui doit être contenu dans la liste à la fin du if?
    La réponse est la suivante : au minimum un Long, mais ça peut être n'importe quoi au dessus... Du coup dans le doute, on n'autorise l'ajout que d'éléments de types Long.
    Et si on fait un get, vu que ça peut être n'importe quoi depuis Long jusqu'à Object, on sait qu'on doit récupérer un Object.



    Il faut savoir que c'est une limitation imposée par le compilateur pour permettre de s'assurer de la concordance des types et d'éviter de se retrouver avec ce qu'on avait avant la version 1.5 de Java, à savoir des Collections qui pouvaient contenir tout et n'importe quoi, et à toi dans ton code de faire attention à bien récupérer les bons éléments et à ne pas en ajouter de mauvais.
    Une fois le code compilé, toutes ces limitations disparaissent et toutes les listes redeviennent de simples Collections non typées (type erasure : le type n'est conservé que pour la compilation et pas pour l'exécution, ça ne sert que de protection et d'aide au codage)
    ClassCastException, la deuxième plus grande source d'erreur après le NullPointerException à cette époque !

  4. #4
    Membre habitué
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2010
    Messages
    212
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2010
    Messages : 212
    Points : 184
    Points
    184
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    Bref, tu imagine que List<? super Number> ne peux contenir que des parent de number (et du coup pas Float,Integer, ...)
    . oui tout a fait c'est çà ce que je croyais.

    Citation Envoyé par eulbobo
    Est-ce que tu peux me dire ce qui doit être contenu dans la liste à la fin du if?
    La réponse est la suivante : au minimum un Long, mais ça peut être n'importe quoi au dessus...
    normalement exactement un Long!! n'est ce pas.

    Maintenant, prenons le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Main {
     
        public static void main(String[] args) {
            List<Object> liste1 = new ArrayList<Object>();
            List<? super Integer> autreliste = liste1;
            autreliste.add(1);
            liste1.add("two");
            //autreliste.add("three"); // will never compile!!.
            System.out.println("liste1:  " + liste1.toString());
            System.out.println("autreliste:  " + autreliste.toString());
     
        }
     
    }
    output:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    liste1:  [1, two]
    autreliste:  [1, two]
    l'affichage est étrange n'est ce pas.
    d'un coté: autreliste n'accepte pas d'ajouter des objets à savoir la chaine "three" et de l'autre coté, il pointe vers une liste contenant des objets.

  5. #5
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Citation Envoyé par win_ubuntu Voir le message
    l'affichage est étrange n'est ce pas.
    d'un coté: autreliste n'accepte pas d'ajouter des objets à savoir la chaine "three" et de l'autre coté, il pointe vers une liste contenant des objets.
    Non, l'affichage n'est pas étrange. Et il n'y a pas "d'un coté" et "d'autre coté".
    Deja, pour commencer, il faut comprendre quel est l'interet d'une Liste<? super XXX> :
    C'est simplement un contrat qui veut dire "dans cette liste, je ne connais pas le type de donnée mais je peux inserer des XXX (ou enfants)".
    Imagines une fonction qui ressemble à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void maFonction(List<? super Integer> maListe)
    {
      for(int i = 0; i < 5; ++i)
      {
        maListe.add(i);
      }
    }
    Tu vas donc pouvoir appeler maFonction avec comme parametre un ArrayList<Object> mais aussi un ArrayList<Integer>. Donc, si tu faisais un maListe.add("coucou"); ca reviendrait à inserer un String dans une liste d'Integer, ce qui n'est pas autorisé.

    La, tu mets le doigt sur les problematiques de List et c'est loin d'etre intuitif. C'est d'ailleurs la meme logique qui fait que tu ne peux pas appeler une fonction "void maFonction(List<Number> list)" avec un objet de type List<Integer> (alors que ca peut sembler assez intuitif).

  6. #6
    Membre chevronné
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Points : 1 993
    Points
    1 993
    Par défaut
    Citation Envoyé par win_ubuntu Voir le message
    normalement exactement un Long!! n'est ce pas.
    Non : à MINIMA un long, mais ça peut contenir n'importe quoi dans la hiérarchie de classe supérieure

    Pour ton code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Main {
     
        public static void main(String[] args) {
            List<Object> liste1 = new ArrayList<Object>(); // tu déclares une liste de type Object
            List<? super Integer> autreliste = liste1; // tu définis la même liste comme une liste contenant A MINIMA des Integer. Une List<Object> est une liste valide pour remplir ce contrat.
            autreliste.add(1); // ici, tu utilises le contrat d'ajout pour autreListe, à savoir tu ne peux ajouter QUE des Integer (parce que tu sais pas définition que tu ne peux ajouter que ça)
            liste1.add("two"); // Ici, tu ajoutes un String dans une liste qui contient des Object, le contrat est respecté
     
            System.out.println("liste1:  " + liste1.toString());
            System.out.println("autreliste:  " + autreliste.toString());
        }
    }
    output:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    liste1:  [1, two]
    autreliste:  [1, two]
    L'affichage n'est pas étrange du tout et permet de mettre le doigt sur un point important : le type contenu dans une liste est une limitation qui est mise en place avant la compilation pour permettre de s'assurer de la cohérence des types de données passées.
    Dans ton code, ce que tu prouves, c'est que le type des listes n'est pas conservé à l'exécution (type erasure) et que toutes les listes se comportent comme des listes d'Object (comme avant les génériques en fait).

    Allons plus loin avec tes deux listes... Si tu fais une boucle sur chacune des listes, que peux tu faire et quelles actions peux-tu entreprendre sur les objets qui sont contenus?

    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 Main {
     
        public static void main(String[] args) {
            List<Object> liste1 = new ArrayList<Object>(); // tu déclares une liste de type Object
            List<? super Integer> autreliste = liste1; // tu définis la même liste comme une liste contenant A MINIMA des Integer. Une List<Object> est une liste valide pour remplir ce contrat.
            autreliste.add(1); // ici, tu utilises le contrat d'ajout pour autreListe, à savoir tu ne peux ajouter QUE des Integer (parce que tu sais pas définition que tu ne peux ajouter que ça)
            liste1.add("two"); // Ici, tu ajoutes un String dans une liste qui contient des Object, le contrat est respecté
     
            System.out.println("liste1:  " + liste1.toString());
            System.out.println("autreliste:  " + autreliste.toString());
            for (int i = 0; i < liste1.size(); i++){
                    Object o1 = liste1.get(i); // ici, tu ne peux récupérer que des types Object comme indiqué dans ta déclaration
                    Object o2 = autreliste.get(i); // et ici aussi ! Tu as une liste qui contient à Minima des Integer, donc ça peut monter jusqu'au type Object. Du coup, à ce moment là, comme tu ne sais pas ce que ta liste "peut" contenir, tu ne peux récupérer que du type Object
                    System.out.println(o1.equals(o2)); // et les éléments des listes récupérés sont toujours strictement égaux...
                    System.out.println(o1 == o2); // Vraiment strictement égaux
            }
        }
    }

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    Citation Envoyé par win_ubuntu Voir le message
    normalement exactement un Long!! n'est ce pas.
    Non justement, ce que tu peux trouver dans la liste dépend de macondition et après tu n'en sais rien. Tout ce qu'on te garanti c'est que tu pourra y mettre des Long, mais tu ignore ce qui se trouve dedans. get() peux très bien te retourner des Float, des String ou des File. Bon, mon exemple était un peu pipoté par manque de temps, je te montre une version agrandie. Mylist est déclaré toujours de la même manière, mais cette fois ci j'y met des données

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    		List<? super Long> myList;
    		if (myCondition){
    		     List<Object> autreListe = new ArrayList<Object>();
    		     autreListe.add("Bonjour");
    		     autreListe.add(new File("c:\\temp"));
    		     autreListe.add(new Float(32.5));
    		     myList = autreListe;
    		} else {
    		     List<Object> autreListe = new ArrayList<Number>();
    		     autreListe.add(new Long(36));
    		     autreListe.add(new Float(32.5));
    		     myList = autreListe;
    		}
    Seule garantie, quand je passe un paramètre à myList, Long est un substitut valide pour ?, mais ce n'est qu'une borne supérieure, d'autres types pourraient être valides si on en savait plus et en retour de méthode, je peux recevoir tout et n'importe quoi. Autre exemple pour que tu comprenne ce que vois le compilateur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    		List<? super Long> myList;
    		if (myCondition){		     
    		     myList = ....;
    		} else {
    		     myList = ....;
    		}
    Et là le compilo doit décider ce qu'on peux faire à la sortie du if avec "myList". C'est limité hein .

    Citation Envoyé par win_ubuntu Voir le message
    l'affichage est étrange n'est ce pas.
    d'un coté: autreliste n'accepte pas d'ajouter des objets à savoir la chaine "three" et de l'autre coté, il pointe vers une liste contenant des objets.
    Les deux déclaration sont différentes.
    avec liste1 tu connais son type exact et ses limites exactes: Object. On peux donc passer des Object en paramètre générique et recevoir des Object en retour générique.
    avec autreliste, tu as rajouté une inconnue avec une borne super: Integer. Tu sais donc que tu peux passer des Integer en paramètres et c'est donc tout ce que le compilateur te laisse faire. Par contre il n'a pas la moindre idée de ce que retourne la méthode get().
    autreliste n'est jamais que la même liste mais avec moins de libertés pour le compilateur car plus d'inconnues.

    Pour faire simple, dans la plupart des cas:
    la déclaration "? super XXX" te permet de connaitre une borne sur ce que tu peux passer en paramètre des méthodes
    la déclaration "? extends XXX" te permet de connaitre une borne sur ce que tu peux recevoir en paramètre de méthodes

    Il y a une règle simple pour les generics en java: PECS

    le "producteur" de donnée -> utilise "extends", consomateur de données -> utilise "super"

    Voilà un exemple détaillé:

    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
    public class Main {
     
        public static void main(String[] args) {
            List<Number> liste1 = new ArrayList<Number>();
            List<? super Integer> listeSuper = liste1;
            List<? extends Integer> listeExtends = liste1;
            liste1.add(12345); //OK c'est une liste de nombres
            liste1.add(6.7); //OK c'est une liste de nombres
            Number number1 = liste1.get(0); //OK c'est une liste de nombre. Note que du coup je ne sais pas que le get(0) est un entier sans tester.
     
            listeSuper.add(67);//ok 67 est bien un entier, c'est cool, on peut ajouter des données
            listeSuper.add(6.9);//ERREUR 6.9 n'est pas un entier, le compilateur n'a pas la garantie qu'il est autorisé dans la liste (même si dans les fait c'est une liste de Number!). Donc on devra se contenter d'ajouter des entier
            Number number2 = listeSuper.get(0); //ERREUR: le compilateur ne peux pas garantir que toute la liste ne contient que des Number!
            Object o1 = listeSuper.get(0); //OK: on va devoir se contenter de récupérer des objets, pas d'autre garantie dans le contrat <? super Integer>!
     
     
            listeExtends .add(67);//ERREUR, le compilateur ne sais pas si c'est une List<Number>, de <Float> de <Long> .... donc il ne peut pas garantir que Integer rentre dedans
            listeExtends .add(6.9);//ERREUR, le compilateur ne sais pas si c'est une List<Number>, de <Float> de <Long> .... donc il ne peut pas garantir que Float rentre dedans
            int integer1 = listeSuper.get(0); //OK: par contre, c'est cool, je peux être sûr de récupérer des Integer depuis la liste, pas besoin de typecaster depuis objet
     
        }
     
    }

  8. #8
    Membre habitué
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2010
    Messages
    212
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Avril 2010
    Messages : 212
    Points : 184
    Points
    184
    Par défaut
    Citation Envoyé par eulbobo
    Non : à MINIMA un long, mais ça peut contenir n'importe quoi dans la hiérarchie de classe supérieure
    oui tout a fait. j'ai mal compris et exprimé. j'ai dû écrire : la méthode add () de cette liste permet d'ajouter seulement des long.
    Citation Envoyé par tchize_ Voir le message
    List<Object> autreListe = new ArrayList<Number>();
    cette instruction ne compile pas, le generic est invariant (au contraire aux tableaux qui sont covariant ). je sais c'est une erreur de frappe. vous avez dû écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    List<Number> autreListe = new ArrayList<Number>();
    la même chose pour:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      List<? extends Integer> listeExtends = liste1;
    en tout cas merci les gars, vous m'avez éclairé les choses.

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    Citation Envoyé par win_ubuntu Voir le message
    . je sais c'est une erreur de frappe. vous avez dû écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    List<Number> autreListe = new ArrayList<Number>();
    Exactement, j'ai un peu trop copié collé dans mon exemple

  10. #10
    Membre chevronné
    Avatar de eulbobo
    Homme Profil pro
    Développeur Java
    Inscrit en
    Novembre 2003
    Messages
    786
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2003
    Messages : 786
    Points : 1 993
    Points
    1 993
    Par défaut
    Fallait être encore plus feignant et écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    List<Number> autreListe = new ArrayList<>();

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Points : 48 804
    Points
    48 804
    Par défaut
    je suis toujours en java 5 dans ma tête

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

Discussions similaires

  1. [JAVA] Gestionnaire de mot de passe
    Par Guimoy dans le forum Mon programme
    Réponses: 3
    Dernier message: 09/02/2016, 11h40
  2. Généricité développement Java
    Par debloc dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 12/12/2012, 11h01
  3. Comment utiliser la généricité en java ?
    Par Adorien dans le forum Langage
    Réponses: 3
    Dernier message: 27/04/2010, 12h35
  4. Généricité en Java (classe avec paramètre)
    Par epcilone059 dans le forum Langage
    Réponses: 1
    Dernier message: 27/03/2009, 12h05
  5. Généricité en java
    Par NewbiZ dans le forum Général Java
    Réponses: 6
    Dernier message: 11/02/2009, 12h27

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