Generics 201
Los Generics son un invento maravilloso hasta que nos encontramos con que no tienen un comportamiento demasiado polimorfico respecto a los objetos que queremos agregar a las colecciones. Suponiendo que partimos de esta jerarquia.
class Animal { }
class Perro extends Animal { }
class Gato extends Animal { }
Las declaraciones
List<Animal> animales1 = new Arraylist<Perro>(); List<Animal> animales2 = new Arraylist<Gato>();
Son horriblemente invalidas.El compilador unicamente va a aceptar que animales1 o animales2 apunten a Lists de Animal! De hecho podemos declarar lo siguiente que seguiria siendo invalido.
public void vacunar(List<Animal>) {
//some code here...
}
List<Perro> perros = new ArrayList<Perro>();
vacunar(perros);
El compilador unicamente admite que el parametro pasado a vacunar() sea de tipo List<Animal> nada de polimorfismo ni cosas raras. Esto nos hace dudar de las ventajas de los generics cuando lo que queremos hacer es caminar una colección de animales buscando un comportamiento polimórfico. Bueno, el porque de esta restricción es algo que puede no ser muy fácil de ver al principio. Supongamos que en lugar de hacer todo esto usando colecciones con generics usamos los viejos y queridos arrays.
Animal[] animales = new Perro[5]; //perfectamente legal. vacunar(animales); //mando Animales a vacunar, en realidad le mando Perros pero sigue siendo legal
Ahora suponiendo que declaramos un metodo vacunar:
public void vacunar(Animal[] animales) {
animales[4] = new Gato();
}
Si bien compila perfectamente, no puedo meter un Gato en un array de Perros, en tiempo de ejecución nos dejaria un gran ArrayStoreException en la pantalla. La idea de los Generics es evitar errores en tiempo de ejecución por hacer chanchadas con las colecciones declarando a priori sobre que clase queremos trabajar. Si se permitiera un comportamiento similar al que hicimos recien con los arrays estariamos tirando por la borda el espiritu de los Generics. ¿Qué hacer entonces?
Bueno, el problema evidentemente esta cuando un metodo trata de agregar algo que no corresponde en una coleccion. Para evitar esto y poder seguir usando colecciones con objetos polimorficos java nos provee de un wildcard “?“
Vamos a cambiar el metodo vacunar para utilizar Generics de otra forma
public void vacunar(List<? extends Animal>) {
//some code here...
}
Ahora la llamada a vacunar(perros) es perfectamente valida! “? extends Animal” le dice al compilador “Como parametro te voy a pasar un List que puede tener objetos de cualquier clase que extienda Animal (o que implemente una Interface Animal), sin embargo SOY CONCIENTE DE LOS QUILOMBOS QUE SE PUEDEN HACER AGREGANDO OBJETOS DE OTRA RAMA DE LA JERARQUIA A LA COLECCION ASI QUE NO VOY A AGREGAR NADA USANDO ADD”.
De hecho, el compilador nos va a dar un error, bastante extraño por cierto, si intentamos utilizar el metodo add con la coleccion que pasamos como parametro. Amazing verdad? Ahora podriamos caminar tranquilamente la coleccion para vacunar a todos los perros y gatos, recordando siempre que NO PODEMOS MODIFICAR LA COLECCIÓN, solo recorrerla.
Esto se pone cada vez mejor pero ¿Qué pasa aun asi quiero poder agregar algo a la coleccion utilizando add? Evidentemente Generics nos va a impedir hacer chanchadas como las que se vieron mas arriba entre perros y gatos. Contamos con una variante en la declaracion.
public void vacunar(List<? super Perro> animales) {
//some code here....
}
La palabrita “super” le dice al compilador “Ok, soy conciente de los quilombos que puedo hacer al agregar cosas raras a las colecciones, pero dejame pasar una coleccion de objetos CUYA CLASE SEA SUPERCLASE DE PERRO”. Lo loco de todo esto es que ahora las jerarquias las usamos en el orden inverso (partimos de una subclase y vamos hacia arriba) al que estamos acostumbrados (siempre ir especializando en subclases). el “? super Animal” me permite agregar, dentro del metodo vacunar, objetos a la coleccion usando add pero unicamente si esos objetos son de clase Perro o de una superclase. De esta forma si declaramos un metodo que reciba una coleccion de “? super Perro”
public void vacunarPerritos(List<? super Perro> perritos) {
perritos.add(new Perro()); //legal
perritos.add(new Animal()); //legal!
perritos.add(new Object()); //legal!
perritos.add(new Gato()); //Error de compilacion
}
El chiste es que ahora perritos puede ser un List de Perro, Animal u Object, y aún así agregarle objetos de clase Perro. Siempre que metamos algo que no sea de clase Perro vamos a tener un error de compilacion evitando de esta forma tener que renegar con RuntimeExceptions que aparecen de la nada.
¿Les siguen gustando los generics?
¡Hola!
Firmaste mi blog esta tarde. La verdad que cuando dijiste de que las preguntas del examen real no son tan difíciles como las del libro fue un gran gran gran alivio. Ya que en el masterexam que viene con el libro no logré más del 60% en el primer intento.
Ah, por cierto, esta info. sobre genéricos me viene muy bien
estaba teniendo problemas para responder algunas preguntas con respecto a ellos.
¡Saludos y gracias por el comentario!
- Just Sherekan -
Comentado por Just Sherekan
— 28/02/2008 #