9 ago 2011

Java Generics para argumentos con herencia e interfaces

Este es un ejemplo claro de esas situaciones que fácilmente podemos dedicarle tres o cuatro horas en resolverlo si no tenemos muy en claro algunas cosillas de Java, me refiero concretamente a los Generics y su uso para validar argumentos.
En GWT la herencia es constate, así como el uso profuso de interfaces, por lo que es muy habitual tener métodos que reciben este tipo de objetos como argumento, esto no es problema en si mismo, el tema es cuando tenemos varias clases diferentes que implementan herencia e interfaces en común y queremos ejecutar métodos definidos en ambas, de que tipo definimos nuestro argumento que hace referencia a todas ellas?

Pongamos un ejemplo, estamos creando una clase MiVista que extiende de Composite e implementa una interfaz definida por nosotros para asegurarnos que estén presentes ciertas operaciones, si un método X recibe este tipo de clases como argumento simplemente podemos definirlo del tipo MiVista:

// Interfaz

public interface MyInterface {
    public void hacerAlgo();
}

// Clase personalizada que extiende e implementa

public class MiVista extends Composite implements MyInterface {

    public MiVista(){
        
        // disponible por extender Composite
        initWidget(...); 
    }
  
    // requerido por implementar MyInterface
    public void hacerAlgo() {
        ...
    }
}


// Método en otra clase que recibe MiVista como argumento

public void setearValores(MiVista vista){
    vista.hacerAlgo();
}


Ahora lo interesante, supongamos que tenemos otra clase MiVistaCompact, que define otra serie de características pero también extiende de Composite e implementa MyInterface, de que tipo lo definimos a nuestro argumento vista en el método setearValores?
Si lo definimos como MiVista dejamos fuera a MiVistaCompact y visceversa, si lo hacemos como Composite nos falla ya que esta clase base no implementa MyInterface ni nuestros propios métodos, si nos referimos como MyInterface no tenemos disponibles los métodos de Composite..., y aquí es donde echamos mano de los Generics de Java.
No voy a profundizar en ellos ya que la documentación es abundante, lo que escasea son ejemplos donde lo que queremos es asegurarnos que nuestro tipo de datos posea ambas características, lo redefinimos así:

public <T extends Composite & MyInterface> void setearValores(T vista){
    vista.hacerAlgo();
}


Lo que estamos haciendo es especificar un nuevo tipo de parámetro en la misma definición del método y referirnos a este como tipo para el argumento, nuestro valor T es ahora cualquier objeto (con ciertas limitaciones) que extienda de Composite e implemente MyInterface, por eso podemos utilizarlo indistintamente para referirnos tanto a MiVista como a MiVistaCompact.
Cuidado al detalle de que solo podemos hacer uso de los métodos disponibles en Composite o MyInterface, no a los específicos de MiVista o MiVistaCompact, pero para esto hacemos un casteo y podemos referirnos a ellos asi:

	public <T extends Composite & MyInterface> void setearValores(T vista){

	    vista.hacerAlgo();  // disponible por implementar MyInterface
	    vista.isAttached(); // disponible por extender Composite

        // disponible solo en MiVista
        ((MiVista)vista).metodoXenMiVista();

        // disponible solo en MiVistaCompact
        ((MiVistaCompact)vista).metodoYenMiVistaCompact();
	}


Lo único a tener en cuenta al ejecutar estos métodos específicos es verificar que estamos recibiendo la clase que los defina, ya que en caso contrario fallará, también vale recordar que para nombrar a nuestro nuevo tipo hay algunas convenciones, copypasteo desde Oracle gentypes:

By convention, type parameter names are single, uppercase letters.
...
The most commonly used type parameter names are:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types


La sintaxis básica es esta, si utilizamos varias interfaces las separamos por comas y según que implementemos las opciones se multiplican ya que tenemos además comodines y jeroglíficos varios, pero para todo eso hay muy buenas guías disponibles y una búsqueda corta seguro nos da para leer un buen rato, aunque repito, en este caso concreto me costo un poco encontrarle la vuelta, no queda otra que leerse unos cuantos tutoriales, impacientes abstenerse.

Solo para los más duros una pequeña guía: GenericsFAQ


No hay comentarios: