JDK 7: l'opérateur diamant

Project Coin fournit de nombreuses "petites améliorations linguistiques" en tant que sous-ensemble des nouvelles fonctionnalités du JDK 7. J'ai récemment blogué sur le basculement de Project Coin sur Strings et dans cet article, j'écris sur le nouvel opérateur Diamond ( ).

L'opérateur Diamond réduit une partie de la verbosité de Java entourant les génériques en permettant au compilateur de déduire des types de paramètres pour les constructeurs de classes génériques. La proposition originale d'ajout de Diamond Operator au langage Java a été faite en février 2009 et comprend cet exemple simple:

Par exemple, considérez l'instruction d'affectation suivante:

Carte anagrammes = nouveau HashMap ();

C'est plutôt long, donc il peut être remplacé par ceci:

Carte anagrammes = nouveau HashMap ();

L'exemple ci-dessus fourni dans la proposition de Jeremy Manson (qui était l'une des premières en réponse à un appel à projets d'idées de pièces de monnaie) est simple, mais démontre de manière adéquate comment l'opérateur diamant est appliqué dans JDK 7. La proposition de Manson explique également pourquoi cet ajout était souhaitable:

L'exigence que les paramètres de type soient dupliqués inutilement comme

cela encourage un malheureux

surabondance de méthodes de fabrique statiques, simplement parce que l'inférence de type

fonctionne sur les invocations de méthode.

En d'autres termes, l'ajout de JDK 7 Project Coin d'un Diamond Operator apporte l'inférence de type aux constructeurs qui était disponible avec des méthodes. Avec les méthodes, l'inférence de type est implicitement effectuée lorsque l'on oublie la spécification de type de paramètre explicite. Avec l'instanciation, en revanche, l'opérateur losange doit être spécifié explicitement pour "dire" au compilateur de déduire le type.

Dans sa proposition initiale, Manson souligne que la syntaxe sans opérateur diamant spécial ne peut pas être utilisée pour inférer implicitement des types pour les instanciations car "à des fins de compatibilité descendante, le nouveau Map () indique un type brut, et ne peut donc pas être utilisé pour le type inférence." La page Inférence de type de la leçon sur les génériques de la piste d'apprentissage du langage Java des didacticiels Java comprend une section intitulée «Inférence de type et instanciation de classes génériques» qui a déjà été mise à jour pour refléter Java SE 7. Cette section décrit également pourquoi le L'opérateur doit être spécifié pour informer explicitement le compilateur d'utiliser l'inférence de type lors de l'instanciation:

Notez que pour tirer parti de l'inférence de type automatique lors de l'instanciation de classe générique, vous devez spécifier l'opérateur losange. Dans l'exemple suivant, le compilateur génère un avertissement de conversion non cochée car le constructeur HashMap () fait référence au type brut HashMap, pas au Map type

Dans le point 24 («Éliminer les avertissements non vérifiés») de la deuxième édition de Effective Java, Josh Bloch souligne en gras , «Éliminez tous les avertissements non vérifiés que vous pouvez». Bloch montre un exemple de l'avertissement de conversion non vérifiée qui se produit quand on compile du code qui utilise un type brut sur le côté droit d'une déclaration. La liste de codes suivante montre le code qui conduira à cet avertissement.

final Map
     
       statesToCities = new HashMap(); // raw! 
     

Les deux instantanés d'écran suivants montrent la réponse du compilateur à la ligne de code ci-dessus. La première image montre le message lorsqu'il n'y a pas d'avertissements -Xlint activés et la seconde montre l'avertissement plus explicite qui se produit lorsque -Xlint:uncheckedest fourni comme argument à javac.

S'il s'agit d' un Java efficace , Bloch souligne que cet avertissement non vérifié particulier est facile à adresser en fournissant explicitement le type de paramètre à l'instanciation de la classe générique. Avec JDK 7, ce sera encore plus facile! Au lieu de devoir ajouter le texte explicite avec ces noms de type, les types peuvent être déduits dans de nombreux cas et la spécification de l'opérateur losange indique au compilateur de faire cette inférence plutôt que d'utiliser le type brut.

La liste de code Java suivante fournit des exemples simplistes de ces concepts. Il existe des méthodes qui démontrent l'instanciation d'un ensemble brut, l'instanciation d'un ensemble avec une spécification explicite de son type de paramètre et l'instanciation d'un ensemble avec un type de paramètre inféré en raison de la spécification de l'opérateur diamant ( ).

package dustin.examples; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static java.lang.System.out; /** * Very simple demonstration of JDK 7's/Project Coin's "Diamond Operator." */ public class DiamondOperatorDemo { /** Use of "raw" type. */ private static Set rawWithoutExplicitTyping() { final Set names = new HashSet(); addNames(names); return names; } /** Explicitly specifying generic class's instantiation parameter type. */ private static Set explicitTypingExplicitlySpecified() { final Set names = new HashSet(); addNames(names); return names; } /** * Inferring generic class's instantiation parameter type with JDK 7's * 'Diamond Operator.' */ private static Set explicitTypingInferredWithDiamond() { final Set names = new HashSet(); addNames(names); return names; } private static void addNames(final Set namesToAddTo) { namesToAddTo.add("Dustin"); namesToAddTo.add("Rett"); namesToAddTo.add("Homer"); } /** * Main executable function. */ public static void main(final String[] arguments) { out.println(rawWithoutExplicitTyping()); out.println(explicitTypingExplicitlySpecified()); out.println(explicitTypingInferredWithDiamond()); } } 

Lorsque le code ci-dessus est compilé, seul le cas "brut" conduit à un avertissement.

À ce stade, il peut être utile de regarder ce que javap nous dit sur ces trois méthodes. Cela se fait dans ce cas avec la commande (l' -voption pour verbose donne tous les détails juteux et -paffiche ces détails juteux pour les privateméthodes):

javap -v -p -classpath classes dustin.examples.DiamondOperatorDemo 

Étant donné que ces méthodes étaient toutes dans une seule classe, il existe un seul flux de sortie pour toute la classe. Cependant, pour faciliter leur comparaison, j'ai copié-collé la sortie dans un format qui aligne la sortie javap de chaque méthode les unes par rapport aux autres. Chaque colonne représente la javapsortie de l'une des méthodes. J'ai changé la couleur de police de la méthode particulière en bleu pour la faire ressortir et étiqueter la sortie de cette colonne.

À part les noms des méthodes elles-mêmes, il n'y a AUCUNE différence dans la javapsortie. En effet, l'effacement des types génériques Java signifie que la différenciation basée sur le type n'est pas disponible au moment de l'exécution. Le didacticiel Java sur les génériques comprend une page appelée Effacement de type qui explique ceci:

Le compilateur supprime toutes les informations sur l'argument de type réel au moment de la compilation.

L'effacement de type existe afin que le nouveau code puisse continuer à s'interfacer avec le code hérité. L'utilisation d'un type brut pour toute autre raison est considérée comme une mauvaise pratique de programmation et doit être évitée autant que possible.

Comme la citation ci-dessus nous le rappelle, l'effacement signifie que le bytecode d'un type brut n'est pas différent d'un type de paramètre explicitement typé, mais encourage également les développeurs à ne pas utiliser de types bruts sauf pour l'intégration avec du code hérité.

Conclusion

L'inclusion de l'opérateur diamant ( ) dans Java SE 7 signifie que le code qui instancie des classes génériques peut être moins détaillé. Les langages de codage en général, et Java en particulier, s'orientent vers des idées telles que la convention sur la configuration, la configuration par exception et la déduction de choses aussi souvent que possible plutôt que d'exiger une spécification explicite. Les langages à typage dynamique sont bien connus pour l'inférence de type, mais même Java à typage statique peut faire plus de cela qu'il ne le fait et l'opérateur losange en est un exemple.

Publication originale disponible sur //marxsoftware.blogspot.com/

Cette histoire, "JDK 7: The Diamond Operator" a été initialement publiée par JavaWorld.