JDK 7: El operador de diamantes

Project Coin proporciona numerosas "pequeñas mejoras de lenguaje" como un subconjunto de las nuevas funciones de JDK 7. Recientemente escribí en un blog sobre el cambio de cuerdas de Project Coin y en esta publicación escribo sobre el nuevo Diamond Operator ( ).

Diamond Operator reduce parte de la verbosidad de Java que rodea a los genéricos al hacer que el compilador infiera tipos de parámetros para constructores de clases genéricas. La propuesta original para agregar Diamond Operator al lenguaje Java se realizó en febrero de 2009 e incluye este simple ejemplo:

Por ejemplo, considere la siguiente declaración de asignación:

Mapa anagramas = nuevo HashMap ();

Esto es bastante largo, por lo que se puede reemplazar con esto:

Mapa anagramas = nuevo HashMap ();

El ejemplo anterior proporcionado en la propuesta de Jeremy Manson (que fue uno de los primeros en respuesta a una convocatoria de ideas para Project Coin) es simple, pero demuestra de manera adecuada cómo se aplica el Operador Diamante en JDK 7. La propuesta de Manson también explica por qué esta adición era deseable:

El requisito de que los parámetros de tipo se dupliquen innecesariamente como

esto anima a un desafortunado

sobreabundancia de métodos de fábrica estáticos, simplemente porque la inferencia de tipos

funciona en invocaciones de métodos.

En otras palabras, la adición de JDK 7 Project Coin de un Diamond Operator brinda una inferencia de tipos a los constructores que ha estado disponible con métodos. Con los métodos, la inferencia de tipo se realiza implícitamente cuando se omite la especificación de tipo de parámetro explícito. Con la instanciación, por otro lado, el operador de diamante debe especificarse explícitamente para "decirle" al compilador que infiera el tipo.

En su propuesta original, Manson señala que la sintaxis sin un operador de diamante especial no podría usarse para inferir implícitamente tipos para instanciaciones porque "para propósitos de compatibilidad con versiones anteriores, new Map () indica un tipo sin formato y, por lo tanto, no se puede usar para tipo inferencia." La página de inferencia de tipos de la lección de genéricos de la ruta de aprendizaje del lenguaje Java de los tutoriales de Java incluye una sección llamada "inferencia de tipos e instanciación de clases genéricas" que ya se ha actualizado para reflejar Java SE 7. Esta sección también describe por qué el El operador debe especificarse para informar explícitamente al compilador que use la inferencia de tipo en la instanciación:

Tenga en cuenta que para aprovechar la inferencia automática de tipos durante la instanciación de clases genéricas, debe especificar el operador de diamante. En el siguiente ejemplo, el compilador genera una advertencia de conversión sin marcar porque el constructor HashMap () se refiere al tipo sin formato HashMap, no al mapa. tipo

En el artículo 24 ("Eliminar advertencias sin marcar") de la segunda edición de Java efectivo, Josh Bloch enfatiza en negrita , "Elimine todas las advertencias sin marcar que pueda". Bloch muestra un ejemplo de la advertencia de conversión sin marcar que ocurre cuando uno compila código que usa un tipo sin formato en el lado derecho de una declaración. La siguiente lista de códigos muestra el código que conducirá a esta advertencia.

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

Las siguientes dos instantáneas de pantalla muestran la respuesta del compilador a la línea de código anterior. La primera imagen muestra el mensaje cuando no hay advertencias -Xlint habilitadas y la segunda muestra la advertencia más explícita que se produce cuando -Xlint:uncheckedse proporciona como argumento para javac.

Si es efectivo en Java , Bloch señala que esta advertencia particular sin marcar es fácil de abordar al proporcionar explícitamente el tipo de parámetro para la instanciación de la clase genérica. ¡Con JDK 7, esto será aún más fácil! En lugar de necesitar agregar el texto explícito con estos nombres de tipo, los tipos se pueden inferir en muchos casos y la especificación del operador de diamante le dice al compilador que haga esta inferencia en lugar de usar el tipo sin formato.

La siguiente lista de código Java proporciona ejemplos simplistas de estos conceptos. Hay métodos que demuestran la instanciación de un conjunto sin procesar, la instanciación de un conjunto con especificación explícita de su tipo de parámetro y la instanciación de un conjunto con el tipo de parámetro inferido debido a la especificación del operador de diamante ( ).

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()); } } 

Cuando se compila el código anterior, solo el caso "crudo" conduce a una advertencia.

En este punto, puede resultar interesante observar lo que nos dice javap sobre estos tres métodos. Esto se hace en este caso con el comando (la -vopción para verbose brinda todos los detalles jugosos y -pmuestra estos detalles jugosos para los privatemétodos):

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

Debido a que estos métodos estaban todos en una sola clase, hay un solo flujo de salida para toda la clase. Sin embargo, para que sea más fácil compararlos, corté y pegué la salida en un formato que alinea la salida javap de cada método entre sí. Cada columna representa la javapsalida de uno de los métodos. He cambiado el color de fuente del método en particular a azul para que se destaque y etiquete la salida de esa columna.

Aparte de los nombres de los métodos en sí, NO hay diferencia en la javapsalida. Esto se debe a que el borrado de tipos genéricos de Java significa que la diferenciación basada en el tipo no está disponible en tiempo de ejecución. El Tutorial de Java sobre genéricos incluye una página llamada Borrado de tipos que explica esto:

El compilador elimina toda la información sobre el argumento de tipo real en tiempo de compilación.

El borrado de tipos existe para que el nuevo código pueda seguir interactuando con el código heredado. El uso de un tipo sin formato por cualquier otro motivo se considera una mala práctica de programación y debe evitarse siempre que sea posible.

Como nos recuerda la cita anterior, el borrado significa que codificar en bytes un tipo sin formato no es diferente de un tipo de parámetro escrito explícitamente, pero también alienta a los desarrolladores a no usar tipos sin formato excepto para la integración con código heredado.

Conclusión

La inclusión del operador de diamante ( ) en Java SE 7 significa que el código que crea instancias de clases genéricas puede ser menos detallado. Los lenguajes de codificación en general, y Java en particular, se están moviendo hacia ideas como la convención sobre la configuración, la configuración por excepción e inferir cosas con la mayor frecuencia posible en lugar de requerir una especificación explícita. Los lenguajes de tipado dinámico son bien conocidos por la inferencia de tipos, pero incluso Java de tipado estático puede hacer más de esto y el operador de diamante es un ejemplo de esto.

Publicación original disponible en //marxsoftware.blogspot.com/

Esta historia, "JDK 7: The Diamond Operator" fue publicada originalmente por JavaWorld.