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 :

[théorie] généricité: quelques soucis


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 76
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut [théorie] généricité: quelques soucis
    (bon Wichtounet m'a dit qu'il n'y avait aucun mal à poster des choses théoriques et abstraites donc voici un petit brainstorming -dans ce qui suit remplacer toutes les affirmations par "il me semble que" -).
    J'ai des soucis avec la généricité: le compilateur appliquant la règle "dans le doute abstiens toi" je me retrouve de temps en temps dans des impasses. Je n'ai rien contre les compilateurs pessimistes mais quand même.
    Voici un exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Truc implements Trucable{
     .....
    }
    Iterator<Truc> iterT ; .....
    Iterator<Trucable> iter = iterT; // compile pas : principe d'invariance
    Iterator<? extends Trucable> iter = iterT ; // OK
    Bien .... sauf que c'est pas toujours possible d'avoir la dernière instruction (par exemple dans une classe qui fait "implements Iterable<Trucable>" bon je détaille pas ici pourquoi on ne peut pas toujours se trouver dans la config favorable).
    Le triste de l'histoire c'est que dans ce cas le contrôle de type se trouve vérifié (mais que le compilateur ne l'entend pas de cette oreille). En effet le résultat du next() si il est de type Truc est affectable à un Trucable!
    Certains ont proposé que la définition d'Iterable<X> soit de définir une méthode iterator() qui rende un <? extends X> mais ça ne résout pas tout.


    Quelques principes que je soumet à votre sagacité:
    - si une interface paramétrée en X ParmInterf<X> n'utilise X que pour les résultats de ses méthodes alors:
    * on peut affecter un ParmInterf<Truc> à un ParmInterf<Trucable> (le principe d'invariance ne s'applique pas).
    * si le paramètre X est remplacé par un type paramétré c'est plus compliqué
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ParmInterf<Iterable<Trucable>> <- ParmInterf<Iterable<Truc>> // OK
    ParmInterf<List<Trucable>> <- ParmInterf<ArrayList<Trucable>> //OK
    ParmInterf<List<Trucable>> <- ParmInterf<List<Truc>> // NON! invariance
    en supposant que des propriétés de ce genre soit justes (ce qui n'est pas prouvé) quelles conclusions en tirer?
    Que se passerait-il si on adoptait un compilateur moins dépressif qui marque le code de ParmInterf pour vérifier ensuite des règles sophistiquées d'utilisation?
    Eh bien les codes génériques qui sont déjà passablement obscurs deviendraient totalement illisibles pour le commun des mortels!

    <brainstorming> ... et si il avait alors un dispositif syntaxique (appelons ça une clause d'indulgence) qui permette au programmeur d'indiquer qu'il demande l'application de règles plus sophistiquées?
    d'abord ça permettrait de régler quelques autres situations embarassantes (j'en ai d'autres exemples ) ensuite c'est déjà pas totalement absent de Java .... exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     List<Truc> liste = new ArrayList<Truc>() ;
     List<Trucable> lst = Collections.unmodifiableList(liste );// SE COMPILE PAS
     List<Trucable> lst = Collections.<Trucable>unmodifiableList(liste ); // OK!!
    des idées? des réactions (peut-être suis je complètement en dehors des clous et il faudrait que je fasse réviser mon neurone)?
    merci de votre attention.

  2. #2
    Membre Expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Par défaut
    J'ai pas tellement compris tes interrogations...

    D'autre part:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    List<Trucable> lst = Collections.<Trucable>unmodifiableList(liste);
    n'est pas une syntaxe valide...
    EDIT: Ah j'avais inversé Truc et Trucable... Je ne savais pas que c'était possible de forcer un "cast" comme ceci sur les paramètres génériques de méthodes

  3. #3
    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,


    Intérressant tout ca

    Tout d'abord juste un détail concernant le terme "générique" : tu l'utilises à la place de Generics un peu à tord : la generalisation est un concept de POO lié à l'héritage, et présent depuis la première version de Java (et dans tous les langages objets en général). Tu devrais plutôt utilisé le terme Generics (voir types paramétrés). En effet, les Generics de Java 5.0 permettent simplement d'utiliser la généricité de manière sécurisé afin d'éviter les ClassCastException (le compilateur vérifiera la cohérence du code en quelque sorte).

    Citation Envoyé par professeur shadoko
    J'ai des soucis avec la généricité: le compilateur appliquant la règle "dans le doute abstiens toi" je me retrouve de temps en temps dans des impasses.
    C'est justement un des objectifs des generics : si le compilateur ne génère aucune erreur ni warning, alors l'utilisation de la généricité est correcte et ne devrait pas provoquer d'exception à l'exécution. Du coup le compilateur semble un peu "pessimiste" en effet (les Generics ont introduit un grand nombre de nouveaux warnings/erreurs).




    Concernant ton premier exemple, si tu ne peux pas affecter la référence d'un Iterator<Truc> dans un Iterator<Trucable>, c'est que tu peux te retrouver dans des cas de mauvaise manipulation (pas avec l'interface Iterator qui est relativement simple, mais cela peut arriver dans d'autre cas). Il est toutefois possible de le faire en "supprimant" le type paramétré via un cast (toutefois cela produit quand même un warning préventif) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	Iterator<Truc> iterT = null;
    	Iterator<Trucable> iter = (Iterator)iterT; // compile avec Warning "unchecked"
    Pourquoi ce principe d'invariance sur les générics (alors que cela est absent des tableaux par exemple) ? Tout simplement parce que toutes la vérification des Generics s'effectue à la compilation, et qu'il est donc impossible de vérifier le type exact de la référence dans ce cas là.

    Ainsi si on prend le cas d'une liste d'Integer et d'une liste de Number (je rappele que Integer hérite de Number), le code suivant montre un cas où l'on passe outre les protections des générics (mais on a quand même un warning à la compilation) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    	List<Integer> listInteger = new ArrayList<Integer>();
    	List<Number> listNumber = (List) listInteger; // Warning "unchecked"
     
     
    	listNumber.add(new Double(0.0)); // Compile OK car Double hérite de Number aussi
    	// Par contre c'est une erreur car on ajoute un Double dans une List<Integer>
     
     
    	Integer i = listInteger.get(0); // Compile OK
    	// Mais générera une ClassCastException car la référence correspond à un Double
    	// --> On a casser les Generics :(
    Le pattern <? extends X> permet justement de gérer ce cas "proprement", afin de provoquer une erreur en cas de mauvaise utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	List<Integer> listInteger = new ArrayList<Integer>();
    	List<? extends Number> listNumber = listInteger; // Compile OK
     
    	listNumber.add(new Double(0.0)); // ERREUR (not applicable) !!!
    Bien sûr cela peut sembler complexe ou contraignant, mais cela permet d'être sûr que le code ne génèrera pas de ClassCastException...

    Enfin, un compilateur "moins dépressif" ne pourra pas vérifier tous les cas, et ferait donc perdre l'unique intérêt des Generics : le code sécurisé !

    En effet, si dans les exemples de mon post et de ton post la vérification est assez évidente, cela peut devenir nettement plus complexe (en particulier si l'implémentation est caché) la vérification doit alors être fait à l'exécution.
    Or avec les Generics le type paramétré est perdu à l'exécution (lire Les Generics ne sont pas des Templates comme les autres !).

    Il y a bien des discussions pour conserver les types paramétrés à l'exécution (lire Reified Generics for Java) mais cela entrainera une incompatibilité avec les anciennes API... du coup il n'y a rien d'officiel pour le moment et je ne pense pas que cela sera le cas (en tout cas dans un future proche).








    Quand à ton second exemple, il est un peu différent car le problème ne concerne pas directement les générics, mais il s'agit plutôt d'un problème d'ambiguité pour le compilateur...
    En effet si on regarde la définition de la méthode Collections.unmodifiableList() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public static <T> List<T> unmodifiableList(List<? extends T> list)
    Or lorsqu'on écrit le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	List<Truc> liste = new ArrayList<Truc>() ;
    	List<Trucable> lst = Collections.unmodifiableList(liste);// SE COMPILE PAS
    Cela ne compile pas car le compilateur utilise le type de liste pour paramétrer la méthode, ce qui nous donne T vaut Truc, et donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public static List<Truc> unmodifiableList(List<? extends Truc> list)
    On se retrouve dans le même cas que dans ton premier exemple avec un problème de variance du type paramétré (le type de retour de la méthode est List<Truc> et on l'affecte à un List<Trucable>)...


    Or si le compilateur utiliserait T vaut Trucable, cela fonctionnerait correctement car la signature devient alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public static List<Trucable> unmodifiableList(List<? extends Trucable> list)

    Ainsi la troisième ligne que tu donnes en solution permet simplement d'indiquer au compilateur le type paramétré à utiliser en cas d'ambiguité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    List<Trucable> lst = Collections.<Trucable>unmodifiableList(liste ); // OK!!

    Ce genre de problème peut également survenir avec des appels de méthodes sans Générics, par exemple avec ces deux méthodes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    	public static void method(Comparable c) {
    		System.out.println("Comparable : " + c);
    	}
     
    	public static void method(Number n) {
    		System.out.println("Number : " + n);
    	}
    Lorsque on compile le code suivant on obtiendra obligatoirement une erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    method(new Integer(9));
    En effet le compilateur ne sait pas s'il doit appeler method(Comparable) ou method(Number) puisque le type Integer étend b]Number[/b] et implémente Comparable...

    On est alors obligé de préciser en utilisant un cast :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    method((Number)new Integer(9));



    Pour conclure, je dirais qu'avec les Generics, le compilateur est devenu un peu plus chiant, mais c'est pour la bonne cause : un code plus sécurisé !


    a++

  4. #4
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 76
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    Citation Envoyé par adiGuba
    C'est justement un des objectifs des generics : si le compilateur ne génère aucune erreur ni warning, alors l'utilisation de la généricité est correcte et ne devrait pas provoquer d'exception à l'exécution. Du coup le compilateur semble un peu "pessimiste" en effet (les Generics ont introduit un grand nombre de nouveaux warnings/erreurs).
    là j'ai pas été assez explicite. je veux dire ceci : dans certains cas le respect des règles des generics tels que les voit le compilateur conduit à des impasses. J'ai deux ou trois cas que je ne vais pas détailler ici où on ne peut pas s'en sortir tout simplement parcequ'il n'y a rien de prévu pour ces cas là. Le sens général de mon post est que les generics ont vraiment une implantation remarquable basée sur des reflexions théoriques très approfondies .... mais, (là c'est moi qui suis pessimiste ), je soupçonne qu'il y a des "trous" théorique dans les specs.
    comme souvent dans les langages c'est un compromis opérationnel qui a été retenu, mais je voulais ici susciter des réflexions sur des choses qui auraient peut-être été possibles (je prend plein de précautions oratoires car je ne suis pas moi-même un théoricien capable de prouver formellement des tas de choses compliquées comme ma conjecture sur les propriétés des types paramétrés qui n'ont des paramètres types que comme résultats de méthode -j'aurais aimé avoir le fruit de vos réflexions sur ce sujet-).

  5. #5
    Membre Expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Par défaut
    Citation Envoyé par professeur shadoko
    J'ai deux ou trois cas que je ne vais pas détailler ici où on ne peut pas s'en sortir
    Justement, il serait intéressant que tu les détailles...

  6. #6
    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 ®om
    Justement, il serait intéressant que tu les détailles...
    +1


    Les Generics sont assez strictes et génèrent souvent des erreurs ou des warnings en prévention de possible problème à l'exécution. Mais même dans le cas d'une erreur, il est possible de s'en sortir avec un simple warning.

    Dans ce cas le développeur prend la responsabilité de la cohérence du code (le compilateur s'en dispense via le warning). A la rigueur on peut dans ce cas utiliser l'annotation @SuppressWarning (mais il faut éviter d'en abuser).


    a++

Discussions similaires

  1. quelques soucis avec l'url rewriting
    Par romain_paris dans le forum Apache
    Réponses: 10
    Dernier message: 17/09/2006, 16h51
  2. Quelques soucis avec un lecteur MP3
    Par Guesh13 dans le forum Audio
    Réponses: 3
    Dernier message: 20/02/2006, 14h57
  3. [POSTGRES 8] [INSTALLATION] [DEBIAN] Quelques soucis
    Par julienOriano dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 24/06/2005, 10h55
  4. [Swings]Quelques soucis à l'affichage !
    Par julienOriano dans le forum AWT/Swing
    Réponses: 3
    Dernier message: 23/09/2004, 13h22
  5. quelques soucis avec word 2000
    Par ramchou dans le forum Word
    Réponses: 3
    Dernier message: 06/09/2004, 18h13

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