Dans le texte qui suit, je note les génériques avec des accolades carrées "[ ]" car blogger est une grosse bouse qui ne gère pas l'échappement des balises html. Je n'ai pas non plus réussi à éviter les &s ...
Les generics ont été introduites dans Java pour résoudre un problème qui n’existe QUE parce que le langage est statiquement typé. Cette version « simplifiée » des templates C++ a fait couler beaucoup d’encre, entre autre pour débattre de sa syntaxe particulièrement … pourrie.
Je vous passe la partie « good » de la présentation, les génériques permettant en substance d’écrire moins de code et d’éviter de se tromper en mettant des oranges dans un panier de bananes.
Si on décompile un bout de code qui manipule une List[foo]
Il faut donc écrire foo(List[? extends Fruit]), pour lequel le mot clé extends n’a RIEN à voir avec le sens classique du terme, comme si un mot clé plus spécifique n’avait pas pu être introduit ici, genre le compilo est très con il ne sait pas identifier un mot comme réservé lorsqu’il est dans une expression générique et le laisser libre dans les autres cas … j’ai toujours été ébahi que le mot clé « class » ne puissent être utilisé comme nom de variable, alors qu’il n’est utilisable dans le langage QUE pour des éléments très spécifiques et facilement identifiables. Ca montre que le compilo est basé sur une analyse lexicale puis sémantique à la grand papa, dommage.
Pour copier les oranges d’un panier d’orange dans un panier de fruit il faudra donc écrire : copy(List[T] source, List[? super T] target)
une nouvelle fois en utilisant un mot clé existant dans un contexte ou son sens est complètement détourné et peu intelligible. Nos génériques, avec une syntaxe de m…, sont nées pour mourir à la compilation, ce qui porte le doux nom de Type Erasure. Confronté au même problème, C# a choisi de casser la compatibilité ascendante pour promouvoir la réification de type (essayez de placer ça dans la conversation, ça fait mec intelligent).
L’effacement de type n’est pas systématique tout de même, ainsi un [T extends Comparable] sera traduit dans le bytecode par un type Comparable. Par contre, un [T extends Comparable & Serializable] - et oui, on peut aussi faire ça, le saviez vous ? pas moi - ne verra plus aucune trace du Serializable dans le bytecode. Le « & » dans une expression générique n’est donc pas commutatif ! fun non (ou très con, selon les goûts) ?
On passe rapidement sur l’absence de support pour les types primitifs, qui nous collent ainsi du boxing/unboxing caché, source de NullPointerException, sans doute mon bug préféré. Un petit mot sur l’impossibilité de construire une instance du type paramétré (sauf à bricoler avec de la réflexion), et on enfonce le clou avec cet exemple : partant d’une interface :
EventListener[t] { void onEvent(T evt) }
vous ne pouvez pas créer une classe Foo implements EventListener[String], EventListener[number], car le code Type-Erasé subira une collision de méthode !
Pour conclure, on prend un exemple ou une classe paramétrée Foo[T] tente de maintenir un compteur statique de son nombre d’instance. Le compteur est alors global, et non par type considéré, et comble du bonheur on ne peut même pas écrire Foo[String].getCount() sans se faire insulter par le compilateur.
Bref, si certains arguments touchent à des utilisations peu communes des génériques, le talk aura proposé un tour assez complet de leurs impacts sur Java, aussi bien au runtime que dans le code source, et démontré la nécessité de toujours définir l’usage attendu des arguments de méthode lorsqu’il sont paramétrés, selon qu’on veut en extraire des données (extends) ou en ajouter (super).
2 commentaires:
Je suis en train de parcourir les articles sur JavaOne. Bravo pour le boulot et très intéressant, Nicolas.
Quelques remarques pour contribuer un peu :-) :
* "Je vous passer" => passe
* "mauvaise foie" => foi. D'ailleurs, à ce propos, c'est amusant que tu parles de ça puisqu'en ce moment on en est à la semaine 4 du cours de coursera, et qu'on y explique justement la covariance (Si A hérite de B, alors on peut dire que List[A] hérite de List[B] si List est covariant).
* "classique du termes" => terme
* très spécifique => spécifiques ?
* "ne vera plus" => verra
* gouts => goûts
Baptiste
@Batmat corrigé, merci
Enregistrer un commentaire