Inferencia tipos Java: El operador diamante
El proyecto de este post los puedes descargar pulsando AQUI.
Intro
El operador diamante es básicamente azúcar sintáctico (syntactic sugar) de Java, que nos va a permitir quitarnos cierta verbosidad a la hora de escribir nuestro código. Esta característica fue incluida en Java 7, por lo que solo podréis usarla en versiones superiores a Java 7.
Ya estaba disponible en métodos
Esta sintaxis ya estaba disponible en los métodos, lo que nos aporta Java 7 es traernos esta tecnología a los constructores.
Si nosotros declarabamos en una clase GenericMehodClass
public class GenericMehodClass { public <T> void printElement(T element){ System.out.println(element); } }
Podemos invocarlo
GenericMehodClass genericMehodClass = new GenericMehodClass(); genericMehodClass.<String>printElement("Hola"); genericMehodClass.<Integer>printElement(5);
Pero el compilador es lo suficientemente inteligente como para saber que si lo que le pasamos es un string, no tenemos que repetirselo, por lo que podemos simplemente decirle:
genericMehodClass.printElement("Hola"); genericMehodClass.printElement(5);
Resultado
Hola 5 Hola 5
Operador diamante
El operador diamante es una mejora en la inferencia de tipos en genéricos, dejando que el compilador sea quien determine el tipo de la clase genérica y no el programador, con el consiguiente ahorro de esfuerzo y tiempo que ello suponía. Además como mucha gente pensaba, no aportaba mucho tener a la derecha y a la izquierda duplicados los tipos.
Con Java 6
//Java 6 style List<String> java6List = new ArrayList<String>();
Con java 7
//Java 7 style List<String> java7List = new ArrayList<>();
De esta manera queda menos verboso, el compilador sabe que java7List va a ser de tipo String al estar declarado en la parte izquierda. Los dos ejemplos hacen exactamente lo mismo.
Declaración de clases genéricas
No solo podemos usar las clases genéricas como List, Map, Set, etc. Podemos crearnos las nuestras propias, además complicando todo lo que queramos. En el siguiente ejemplo vamos a ver que nos podemos declarar una clase genérica que sea de tipo U, conteniendo un lista de tipo U y con un constructor de tipo T
import java.util.ArrayList; import java.util.List; public class GenericClass <U> { List<U> list = new ArrayList<>(); <T> GenericClass(T t) throws NoSuchFieldException { System.out.println("T: " + t.getClass().getSimpleName()); } public void add(U element){ list.add(element); System.out.println("U: " + list.get(0).getClass().getSimpleName()); } }
Como vemos la podemos instanciar con el operador diamante:
GenericClass<String> genericClass1 = new GenericClass<>(5); genericClass1.add("Hola");
Si lo ejecutamos nos va a decir:
- T: Integer
- U: String
Ya que hemos dicho que el constructor va a ser Integer, pero la clase String. Sin embargo:
GenericClass genericClass2 = new GenericClass("Hola"); genericClass2.add(5);
Nos dice que el constructor es String, pero la clase Integer.
- T: String
- U: Integer
El ejemplo es rebuscado, es solo para dar una visión de lo que se puede hacer con genéricos en cuanto a inferencia de tipos.
Algunas restricciones
Para poder beneficiarnos de la sintaxis, es obligatorio poner el operador diamante (omitir el tipo a la derecha) siempre que el compilador pueda inferir los tipos del contexto. Recordemos que esto es un avance del compilador que ahora es más “listo”, pero no adivino.
Si volvemos al ejemplo anterior:
Con Java 6
//Java 6 style List<String> java6List = new ArrayList<String>();
Con java 7
//Java 7 style List<String> java7List = new ArrayList<>();
Esa sería la sintaxis correcta, si hacemos
List<String> java7NonDiamondList = new ArrayList();
Nos va a dar un warning
Warning:(26, 43) java: unchecked conversion required: java.util.List found: java.util.ArrayList
Ya que new ArrayList() hace referencia al tipo raw de ArrayList y no a su tipo genérico.
Tener la flexibilidad de tener tipos genericos, no hace que perdamos el tipado fuerte de Java, por ejemplo, en la lista podemos hacer
java7List.add("Hola");
Pero no podemos hacer (en la misma lista)
java7List.add(5);
Pues nos da el siguiente error:
add (java.lang.String) in List cannot be applied to (int)
Consulta. En tu ejemplo public class GenericClass , porque declaras y no …. digo, cual es la que debe declarar la clase, si los metodos utilizan mas de una generica? Gracias!