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 :

[Généricité] Un échec de compilation qui s'explique précisément par?


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif

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

    Informations forums :
    Inscription : Janvier 2007
    Messages : 608
    Par défaut [Généricité] Un échec de compilation qui s'explique précisément par?
    Bonjour,


    J'écrivais un petit code de démonstration, et j'ai rencontré un obstacle inattendu.

    Avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public abstract class Fruit extends Produit implements Périssable {...}
    et
    public class Pomme extends Fruit {...}

    J'ai écrit ceci:
    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 Expérimentation
    {
       public <P extends Fruit> Vector<P> liste()
       {
          Vector<P> liste = new Vector<P>();
     
          Pomme pomme = new Pomme("Pomme rouge");
          liste.add(pomme);
     
          Fruit f = liste.get(0);
     
          return(liste);
       }
    }
    La compilation échoue sur un message "The method add(P) in the type Vector<P> is not applicable for the arguments (Pomme)".

    Et pourtant, Pomme hérite bel et bien de Fruit!
    Je peux essayer une grande variété d'instructions qui fonctionnent et qui le montrent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fruit f = liste.get(0);
    ou
    Vector<? extends Fruit> liste = new Vector<Pomme>();

    Mais le add(pomme) dans le vecteur, lui, ne passe pas.

    Bien entendu, si je modifie le prototype de ma fonction en:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public Vector<Pomme> liste()
    ou
    public Vector<Fruit> liste()
    et que j'adapte son code en conséquence, tout rentrera dans l'ordre. Mais:
    - Ce n'est pas ce que je veux.
    - Cela ne me donnera pas pour autant l'explication du problème de compilation qui se pose.


    J'ai fait l'hypothèse que ce qui empêche Java de compiler mon programme c'est qu'à la compilation il procède au type erasure pour rendre son code binairement compatible 1.4.

    Et qu'alors, il se dit:

    Avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public Vector<Pomme> liste()
     
    je dois simuler cette invocation:
    Vector.add(Pomme p)
    Avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public Vector<Fruit> liste()
     
    je dois simuler cette invocation:
    Vector.add(Fruit f)
    et dans les deux cas, je peux faire une assertion sur le paramètre d'entrée et le simplifier en un type unique.

    Mais avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public <P extends Fruit> Vector<P> liste()
     
    je dois invoquer:
    Vector.add(? extends Fruit f)
    Et là, je ne peux pas remplacer ? extends Fruit par une classe unique puisque cela pourrait être n'importe quelle classe dérivée de Fruit.

    Mais cette explication ne me convient pas entièrement, car ? extends Fruit est une expression qui admet Fruit comme classe.

    Donc, Java devrait pouvoir faire l'assertion que la méthode add(...) à appeler a pour prototype minimal garanti: Vector.add(Fruit f)?


    Quel explication donnez-vous à ce qui se produit?

    Je vous remercie!

    Grunt.

  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 grunt2000 Voir le message
    Donc, Java devrait pouvoir faire l'assertion que la méthode add(...) à appeler a pour prototype minimal garanti: Vector.add(Fruit f)?
    Le compilateur vérifie la cohérence de l'ensemble du code utilisant les Generics, et produit des warnings/erreurs en cas de problème.

    On utilise le wildcard ? extends pour indiquer qu'on ne connait pas le paramétrage précis, ce qui nous donne un accès partiel aux méthodes de la classe Generics.

    Dans ton cas, avec Vector<? extends Fruit> tu peux récupérer les éléments du vecteur (car tu sais que ce seront des Fruits ou un type fils quelconques), par contre tu ne peux pas y ajouter des éléments car tu ne sais pas quel est le type d'objet exact qui est attendu par le vecteur.

    Tu veux y ajouter des Pommes, mais rien ne garantie que la liste ne devrait pas être limité aux Bananes ou aux Oranges par exemple...


    Les wildcard sont surtout utilisé pour faire un code générique qui ne soit pas limiter à un type précis, mais je n'y vois pas vraiment d'intérêt dans ton cas...



    a++

  3. #3
    Membre très actif

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

    Informations forums :
    Inscription : Janvier 2007
    Messages : 608
    Par défaut
    Dans cet extrait, l'intérêt de <P extends Fruit> est faible, mais je voulais à terme rajouter des références à P dans les paramètres d'entrée de la fonction.

    Ce qui m'intrigue, c'est qu'il me semble que Java devrait se sortir de cette situation. Il ne peut déterminer quel est la limite de la classe "en spécialisation" (Banane, Orange, Pomme, etc.) mais il est sûr de celle "en généricité": c'est Fruit. Donc, il devrait pouvoir (à mon sens) l'accepter.
    Car add(Fruit f) est supposé accepter tout ? extends Fruit.

    Je ne vois pas d'ambiguïté, ou de cas où cela pourrait réellement coincer.

    Grunt.

  4. #4
    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 grunt2000 Voir le message
    Je ne vois pas d'ambiguïté, ou de cas où cela pourrait réellement coincer.
    Tu ne connais pas le type précis de la liste, donc tu ne peux pas y ajouter d'élément.

    Par exemple si tu fais ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        List<? extends Fruit> list = ...
        list.add(new Pomme("Pomme rouge")); // Erreur de compilation
    Tu ajoutes une Pomme dans une liste dont tu ne connais pas le paramétrage exact. Le compilateur ne peut donc pas t'assurer que ce code sera valide sans erreur lors de l'exécution, et il génère donc une erreur.

    Ainsi si la liste est déclarée comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        List<? extends Fruit> list = new ArrayList<Pomme>();
    Cela pourrait marcher (si c'était autorisé par le langage).

    Mais si la liste est déclarée comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        List<? extends Fruit> list = new ArrayList<Banane>();
    Ce serait incorrect car tu ajouterais une Pomme dans une liste censé contenir uniquement des Bananes


    Les wildcards te donne une vision limité sur l'objet, c'est normal et c'est voulu puisque tu ne connais pas le type exact.




    Ceci est la base même de l'objectif des Generics : générer un code typesafe qui ne génèrera pas d'erreur de typage à l'exécution.

    C'est le principe inverse des tableaux, avec lesquelles un code peut compiler parfaitement mais générer des erreurs à l'exécution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        Fruit[] fruits = new Pomme[5];
     
        fruits[0] = new Banane(); // java.lang.ArrayStoreException


    Plutôt que de t'entêter à utiliser les wildcards de cette manière, expliques précisément ce que tu voudrais faire...



    a++


    PS : Et il y a une raison pour que tu utilises des Vectors à la place des List/ArrayList ???

  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
    pour montrer simplement le problème, "supposons" que ton code marche, on pourrais alors écrire ceci

    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
    public class Expérimentation
    {
       public <P extends Fruit> Vector<P> liste()
       {
          Vector<P> liste = new Vector<P>();
     
          Pomme pomme = new Pomme("Pomme rouge");
          liste.add(pomme);
     
          Fruit f = liste.get(0);
     
          return(liste);
       }
       // .....
       Vector<Banane> bananes = liste ();
       // et avoir une pomme dans notre liste de bananes? -> ben non c'est pas possible.
    }
    Dans ton cas, le code correct c'est soit
    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 Expérimentation
    {
       public Vector<Fruit> liste()
       {
          Vector<Fruit> liste = new Vector<Fruit>();
     
          Pomme pomme = new Pomme("Pomme rouge");
          liste.add(pomme);
          // on pourrais aussi mettre des bananes si on veux!
     
          return(liste);
       }
    }
    soit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public class Expérimentation{
       public <P extends Fruit> Vector<P> liste(P elements)
       {
          Vector<P> liste = new Vector<P>();
          liste.add(element);
          return(liste);
       }
       // ....
       Vector<Pomme> pommes = liste(new Pomme("une pomme"));
       Vector<Banane> bananes = liste(new Banane("une banane"));
    }

  6. #6
    Membre très actif

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

    Informations forums :
    Inscription : Janvier 2007
    Messages : 608
    Par défaut
    Je souhaiterais atteindre ce prototype final:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public <P extends Fruit> Vector<P> liste(Class<P> classeProduit)
    {
       Vector<P> liste = ... Instanciation du vecteur approprié ...
     
       .. alimentation ..
     
       return(liste);
    }

    Pour pouvoir l'employer ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Vector<Banane> bananes = panier.liste(Banane.class);
    Vector<Pomme> pommes = panier.liste(Pomme.class);

  7. #7
    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 grunt2000 Voir le message
    Je souhaiterais atteindre ce prototype final
    Dans ce cas c'est différent car tu es sûr d'ajouter le bon type dans la liste, car tu ajoutes réellement un objet du type P.

    Bon par contre il faut passer par de la reflection, et j'ai toujours du mal à en saisir l'intérêt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        public <P extends Fruit> Vector<P> liste(Class<P> classeProduit) {
            Vector<P> liste = new Vector<P>();
            try {
                liste.add(classeProduit.newInstance());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return (liste);
        }
    a++

  8. #8
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 585
    Par défaut
    Citation Envoyé par grunt2000 Voir le message
    Car add(Fruit f) est supposé accepter tout ? extends Fruit.
    C'est le contraire, un wildcard ? n'accepte rien. Du tout. Il donne, et donne seulement.

    Quant à P extends Fruit, il accepte en effet des P. Donc on peut lui add(P p). Or un Fruit n'est pas un P. C'est l'inverse : un P est un Fruit.

    Citation Envoyé par grunt2000 Voir le message
    Je ne vois pas d'ambiguïté, ou de cas où cela pourrait réellement coincer.
    En effet, il n'y a pas d'ambiguïté.
    C'est un Vector de P, et on sait que P est une sous-classe de Fruit. Donc un Fruit n'est pas assignable à un P, donc on ne peut pas donner de Fruit au Vector.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Membre très actif

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

    Informations forums :
    Inscription : Janvier 2007
    Messages : 608
    Par défaut
    Citation Envoyé par thelvin Voir le message
    C'est un Vector de P, et on sait que P est une sous-classe de Fruit. Donc un Fruit n'est pas assignable à un P, donc on ne peut pas donner de Fruit au Vector.
    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
    import java.io.File;
     
    public class Toto<X extends File>
    {
       public void ouvrir(X fichier)
       {
          throw(new RuntimeException("Non, je n'ouvrirai pas votre fichier " + fichier.getName()));
       }
    }
     
    public class Expérimentation
    {
       public static void main(String[] args)
       {
          Toto<File> t = new Toto<File>();
          t.ouvrir(new File(""));
       }
    }
    ? extends File accepte File.
    Et c'est bien un P que l'on veut donner au vecteur de P(s).

  10. #10
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 585
    Par défaut
    Citation Envoyé par grunt2000 Voir le message
    ? extends File accepte File.
    Pas du tout.
    Ton code montre qu'un Toto<File> accepte un File.
    Donc File accepte File. Doh !

    Tu ne peux pas expérimenter les paramètres de méthode avec des paramètres de classe.
    Enfin, pas avant de bien comprendre quelles questions se posent pour l'un et quelles questions se posent pour l'autre.


    Citation Envoyé par grunt2000 Voir le message
    Et c'est bien un P que l'on veut donner au vecteur de P(s).
    Moi dans ton code, je t'ai vu lui filer des Pomme, pas des P.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

Discussions similaires

  1. Compilation qui plante
    Par cotede2 dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 21/04/2009, 13h15
  2. Réponses: 3
    Dernier message: 14/04/2009, 12h29
  3. Qui pourrait expliquer mon pb de core dump ?
    Par seb9999 dans le forum C
    Réponses: 10
    Dernier message: 02/03/2007, 19h06
  4. compilation qui s'arrête
    Par Grecko dans le forum Dev-C++
    Réponses: 5
    Dernier message: 08/01/2007, 16h17
  5. Pb de compilation qui me stresse ...
    Par fredoun dans le forum CORBA
    Réponses: 1
    Dernier message: 19/05/2004, 18h46

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