Java 101: visite guidée des fonctionnalités essentielles du langage Java, partie 5

Précédent 1 2 Page 2 Page 2 de 2

Inférence de type et constructeurs génériques pour les classes génériques et non génériques

Les classes génériques et non génériques peuvent déclarer des constructeurs génériques dans lesquels un constructeur a une liste de paramètres de type formel. Par exemple, vous pouvez déclarer la classe générique suivante avec un constructeur générique:

 public class Box { public  Box(T t) { // ... } } 

Cette déclaration spécifie une classe générique Boxavec un paramètre de type formel E. Il spécifie également un constructeur générique avec un paramètre de type formel T. Vous pouvez instancier la classe générique et appeler son constructeur comme suit:

 new Box("Aggies") 

Cette expression crée une instance de Box, passant Marbleà E. En outre, le compilateur déduit Stringcomme Targument de type réel car l'argument du constructeur est un Stringobjet.

Les compilateurs pré-Java 7 déduisent les arguments de type réel d'un constructeur générique de la même manière que ceux d'une méthode générique. Cependant, le compilateur de Java 7 peut déduire les arguments de type réels de la classe générique instanciée dans un contexte d'opérateur diamant. Prenons l'exemple suivant:

 Box box = new Box("Aggies"); 

En plus de déduire le type Marbledu paramètre Ede type formel de la classe générique Box, le compilateur infère le type Stringdu paramètre Tde type formel du constructeur de cette classe générique.

Project Coin small change # 8: Invocation simplifiée de la méthode varargs

Avant Java 7, chaque tentative d'appeler une méthode varargs (arguments variables, également appelée variable arity ) avec un type varargs non réifiable entraînait le compilateur à afficher un avertissement "opération non sûre". Pour éliminer le potentiel de nombreux messages d'avertissement similaires (un par site d'appel), Java 7 a déplacé l'avertissement du site d'appel vers la déclaration de méthode.

Types réifiables et non réifiables

Un type réifiable expose ses informations de type complètes au moment de l'exécution. Les exemples incluent les types primitifs, les types non génériques, les types bruts et les appels de caractères génériques non liés. En revanche, un type non réifiable a des informations de type supprimées au moment de la compilation par effacement de type, pour assurer la compatibilité binaire avec les bibliothèques et applications Java créées avant les génériques. Les exemples incluent Setet Set. Étant donné qu'un type non réifiable n'est pas entièrement disponible au moment de l'exécution, la machine virtuelle Java ne peut pas faire la différence entre Setet Set; au moment de l'exécution, seul le type brut Setest disponible.

Les méthodes génériques qui incluent des paramètres d'entrée vararg peuvent provoquer une pollution du tas , dans laquelle une variable d'un type paramétré fait référence à un objet qui n'est pas de ce type paramétré (par exemple, si un type brut a été mélangé avec un type paramétré). Le compilateur signale un "avertissement non vérifié" car l'exactitude d'une opération impliquant un type paramétré (comme un appel de conversion ou de méthode) ne peut pas être vérifiée.

Le listing 13 montre la pollution en tas dans un contexte non varargs.

Listing 13. Démonstration de la pollution en tas dans un contexte non varargs

 import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class HeapPollutionDemo { public static void main(String[] args) { Set s = new TreeSet(); Set ss = s; // unchecked warning s.add(new Integer(42)); // another unchecked warning Iterator iter = ss.iterator(); while (iter.hasNext()) { String str = iter.next(); // ClassCastException thrown System.out.println(str); } } } 

La variable ssa un type paramétré Set. Lorsque le java.util.Setqui est référencé par sest affecté à ss, le compilateur génère un avertissement non vérifié. Il le fait car le compilateur ne peut pas déterminer qui sfait référence à un Settype (il ne le fait pas). Le résultat est une pollution massive. (Le compilateur permet à cette affectation de préserver la compatibilité descendante avec les anciennes versions de Java qui ne prennent pas en charge les génériques. De plus, l'effacement de type se transforme Seten Set, ce qui entraîne l' Setattribution de l'une à l'autre Set.)

Le compilateur génère un deuxième avertissement non vérifié sur la ligne qui appelle Setla add()méthode de. Il le fait car il ne peut pas déterminer si la variable sfait référence à un type Setou Set. C'est une autre situation de pollution en tas. (Le compilateur autorise cet appel de méthode car l'effacement transforme Setla boolean add(E e)méthode de en boolean add(Object o), qui peut ajouter n'importe quel type d'objet à l'ensemble, y compris le java.lang.Integersous - type de java.lang.Object.)

La pollution en tas peut facilement se produire dans un contexte de varargs. Par exemple, considérez le Listing 14.

Listing 14. Démonstration de la pollution en tas dans un contexte de varargs

 import java.util.Arrays; import java.util.List; public class UnsafeVarargsDemo { public static void main(String[] args) { unsafe(Arrays.asList("A", "B", "C"), Arrays.asList("D", "E", "F")); } static void unsafe(List... l) { Object[] oArray = l; oArray[0] = Arrays.asList(new Double(3.5)); String s = l[0].get(0); } } 

L' Object[] oArray = l;affectation introduit la possibilité de pollution en tas. Une valeur ne correspondant pas au type paramétré du paramètre varargs lpeut être affectée à la variable oArray. Cependant, le compilateur ne génère pas d'avertissement non vérifié car il l'a déjà fait lors de la traduction List... len List[] l. Cette affectation est valide car la variable la le type List[], quels sous-types Object[].

De plus, le compilateur n'émet pas d'avertissement ou d'erreur lors de l'affectation d'un Listobjet de tout type à l'un des oArraycomposants du tableau de; par exemple oArray[0] = Arrays.asList(new Double(3.5));,. Cette affectation est affectée au premier composant de tableau d' oArrayun Listobjet contenant un seul java.lang.Doubleobjet.

L' String s = l[0].get(0);affectation est problématique. L'objet stocké dans le premier composant de tableau de variable la le type List, mais cette affectation attend un objet de type List. En conséquence, la JVM lance java.lang.ClassCastException.

Compilez ce code source ( javac -Xlint:unchecked UnsafeVarargsDemo.java). Vous devez observer la sortie suivante (légèrement reformatée pour plus de lisibilité) lors de la compilation sous Java SE 7 mise à jour 6:

 UnsafeVarargsDemo.java:8: warning: [unchecked] unchecked generic array creation for varargs parameter of type List[] unsafe(Arrays.asList("A", "B", "C"), ^ UnsafeVarargsDemo.java:12: warning: [unchecked] Possible heap pollution from parameterized vararg type List static void unsafe(List... l) ^ 2 warnings 

Dans mon introduction à Java 101 aux génériques, j'ai déclaré que vous ne pouvez pas utiliser de paramètres de type dans les expressions de création de tableau. Par exemple, vous ne pouvez pas spécifier elements = new E[size];. Le compilateur signale un message «d'erreur de création de tableau générique» lorsque vous essayez de le faire. Cependant, il est toujours possible de créer un tableau générique, mais uniquement dans un contexte varargs, et c'est ce que rapporte le premier message d'avertissement. Dans les coulisses, le compilateur se transforme List... len List[] l, puis en List[] l.

Notez que l'avertissement de pollution du tas est généré sur le unsafe()site de déclaration de la méthode. Ce message n'est pas généré sur le site d'appel de cette méthode, ce qui est le cas avec les compilateurs Java 5 et 6.

Toutes les méthodes varargs ne contribueront pas à la pollution en tas. Cependant, un message d'avertissement sera toujours émis sur le site de déclaration de la méthode. Si vous savez que votre méthode ne contribue pas à la pollution du tas, vous pouvez supprimer cet avertissement en le déclarant avec l' @SafeVarargsannotation - Java 7 a introduit le java.lang.SafeVarargstype d'annotation. Par exemple, comme il n'y a aucun moyen pour la méthode de la Arraysclasse de asList()contribuer à la pollution du tas, la déclaration de cette méthode a été annotée avec @SafeVarargs, comme suit:

 @SafeVarargs public static  List asList(T... a) 

L' @SafeVarargsannotation élimine la création de tableau générique et les messages d'avertissement de pollution du tas. C'est une partie documentée du contrat de la méthode et affirme que l'implémentation de la méthode ne gérera pas de manière incorrecte le paramètre formel varargs.

En conclusion

Java 7 a amélioré la productivité des développeurs en introduisant la gestion automatique des ressources via l'instruction try-with-resources avec une nouvelle AutoCloseableinterface, une chaîne de commutation, des captures multiples, une relance finale, des littéraux binaires, des traits de soulignement dans les littéraux numériques, des modifications du type du compilateur algorithme d'inférence qui a introduit le soi-disant opérateur diamant et simplifié l'invocation de la méthode varargs. Prochaine étape dans Java 101: La série nouvelle génération est un aperçu des fonctionnalités du langage lambda et d'interface fonctionnelle de Java 8.

Cette histoire, "Java 101: Visite guidée des fonctionnalités essentielles du langage Java, Partie 5" a été publiée à l'origine par JavaWorld.