Estudiando para dar mi examen de SCJP 5 encontré algo que llaman “genéricos”. Recordarás que ahora las colecciones ahora pueden ser con tipo: * ArrayList
perros=new ArrayList
(); perros.add(new Perro(“Fido”));*
se ve bonito, y luego bueno de esto es que el ** compilador ** no permitirá algo como esto: * perros.add(new Integer(1));*
Así, también se cumple la herencia: * List**
perros=new ** ArrayList**
();*
Pero esto no se puede hacer: * import java.util.; * class Animal{} class Perro extends Animal{}* * public class Test { public static void main(String[] args){ List** Animal** > animales=new ArrayList** Perro** >(); }
}*
¿Por qué? ¡si Perro ES-UN animal! Pues bien, esto corrige un problema que tienen los arreglos comunes:
* import java.util.; class Animal{} class Perro extends Animal{} class Gato extends Animal{} public class Test { public static void main(String[] args){** Perro[] perros=new Perro[]{new Perro()}; //arreglo de perros** agregar(perros); } ** private static void agregar(Animal[] animales) { //es permisible xq Perro ES-UN Animal* animales[0]=new Gato(); //OUCH!! esto no debería ser** }
}*
Compila, y cuando se ejecuta manda una excepción (ArrayStoreException). La cuestión es que las colecciones con tipo asegura que los valores que se les pasa no tengan errores de este tipo al momento de la compilación. Por ello, debe asegurar que sean del mismo tipo-de-la-colección. Entonces, esto no compilaría, porque las colecciones se aseguran que tengan el mismo tipo. * //… public static void main(String[] args){ List
perros=new ArrayList
(); perros.add(new Perro());** agregar(perros); //no se puede aplicar tipos de colecciones diferentes.** }
private static void agregar(List animales) {
animales.add(new Gato());
} //...*
Pero, claro está, ** no vamos a crear tantos métodos cómo subclases tengamos** . Aquí entran los “genéricos”. Se puede declarar parámetros de métodos que sean de tipo (subtipos o supertipos) de una clase en especial. Entonces, para que nuestro método compile aceptando cualquier colección, debería ser así: * //.. public static void main(String[] args){ List
perros=new ArrayList
(); perros.add(new Perro()); agregar(perros); ** List gatos=new ArrayList(); agregar(gatos); //tambien puedo llamar al mismo método**
}
****
private static void agregar(List animales) { //usando genéricos ****
//animales.add(new Gato()); //pero esta línea no va a compilar
for(Animal animal:animales) {
//pero puedo manejar cada elemento por igual
}
} //..*
Ajá, con esto aseguro que solo se permitan manejar todos por igual. Pero, si deseo agregar elementos a la colección? Java no va a permitir cualquier cosa que se agregue. Para ello se puede utilizar la palabra clave ** super** en lugar de ** extends** . Aquí un código más completo para entender cómo funciona: * import java.util.; abstract class Animal{ //la clase Animal, tambien puede ser interface public abstract void dejarseRevisar(); } class Perro extends Animal{ //La implementacion en Perro public void dejarseRevisar() { System.out.println(“El Perro se deja revisar”); }} class Gato extends Animal{ //La implementacion en Gato public void dejarseRevisar() { System.out.println(“El Gato se deja revisar”); }} class Poodle extends Perro{ //La implementacion en Poodle que ES-UN Perro public void dejarseRevisar() { System.out.println(“El Poodle se deja revisar”); }} public class Test { public static void main(String[] args){* Veterinario veterinario=new Veterinario(); //tenemos un Veterninario**
List
perros=new ArrayList
(); //creamos nuestra lista de perros perros.add(new Perro()); //agregamos uno** veterinario.revisar(perros); //lo mandamos a revisar, es tratado como Animal **
List gatos=new ArrayList(); //creamos nuestra lista de gatos
gatos.add(new Gato());****
veterinario.revisar(gatos); //lo mandamos a revisar, es tratado como un Animal ****
List
poodles=new ArrayList
(); //creamos nuestra lista de poodles poodles.add(new Poodle());** veterinario.revisar(poodles); //…. idem…**
List animales=new ArrayList(); //creamos una lista de animales ****
veterinario.agregarPerros(animales); //y lo manejamos solo para perros a pesar que es una coleccion de animales****
}
} class Veterinario{** void revisar(List animales){ //todos son tratados como animales** for(Animal animal:animales) animal.dejarseRevisar(); }** void agregarPerros(List perros){** perros.add(new Perro()); perros.add(new Poodle());** //perros.add(new Gato()); //no lo permite, porque lo vamos a manejar como colección de Perros**
} }*
Bacán, no? (je). además, esto fue un ejemplo para los argumentos, pero también funciona en variables (con ejemplos de los que no pueden ser compilablres): * List list = new ArrayList
(); //compila List aList = new ArrayList
(); //compila** List foo = new ArrayList(); //no compila List cList = new ArrayList(); //no compila porque Integer no ES-UN Perro** List bList = new ArrayList(); //compila** List dList = new ArrayList
(); //no compila porque Perro no es una super-clase de Animal***
Ahora, dirás “yo también quiero tener mi clase que permita genéricos”. Pues es sencillo. Pongamos el escenario: imaginemos que tengamos una lógica para manejar los alquileres de lo que fuere: * import java.util.; class Alquilar{ private List listaAlquiler; private int maxAlquiler; public Alquilar(int max, List lista){ maxAlquiler=max; listaAlquiler=lista; } public Object alquilar(){ //algo que maneje la disponibilidad de alquiler return listaAlquiler.get(0); } public void regresa(Object o){ //regresa el elemento alquilado listaAlquiler.add(o); } }
Ahora, queremos implementar esta lógica para alquilar autos: * class Auto{} class AlquilarAuto extends Alquilar{ public AlquilarAuto(int m,List l){ super(m,l); }
public Object alquilar() {
return super.alquilar();
}
public void regresa(Object o) {
if (o instanceof Auto)
super.regresa(o);
else
System.err.println("Aqui no se devuelve");
} } * Pero, si queremos usar la misma lógica para alquilar casas, uartos, computadoras? ¿tendriamos que extender siempre la misma la clase para hacer solo llamadas al * super* ? Bastaría con tener una misma lógica, y dependiendo de cómo se instancie, debería de comportarse por igual, no? además no deberí haber esa validación de * instanceof* en el método regresa(). * class **** AlquilarGenerico****** **** { //de algun **** T**** ipo****
private List listaAlquiler; //manejará el mismo tipo****
private int maxAlquiler;****
public AlquilarGenerico(int max, ****** List****** lista){ //recibe el mismo tipo con que fue instanciado****
maxAlquiler=max;
listaAlquiler=lista;
}****
public ****** T ****** alquilar(){ //retorna el mismo Tipo****
//algo que maneje la disponibilidad de alquiler
return listaAlquiler.get(0);
}****
public void regresa(****** T o****** ){ //recibe del mismo tipo que fue instanciado****
listaAlquiler.add(o);
} }*
Podría haber sido cualquier letra en lugar de T, pero por convencion, se usa la letra T para referirse a los Tipos. Entonces, ya se podría usar algo así: * //… class Auto{} class Casa{}* * public class Test2 { public static void main(String[] args){ List autos=new ArrayList(); autos.add(new Auto()); AlquilarGenerico alquilarAuto=new AlquilarGenerico(100,autos); Auto a1=alquilarAuto.alquilar(); alquilarAuto.regresa(a1);
List casas=new ArrayList();
casas.add(new Casa());
AlquilarGenerico alquilarCasa=new AlquilarGenerico(100,casas);
Casa c1=alquilarCasa.alquilar();
alquilarCasa.regresa(c1);
}
}* Ahora, no solo depende de cómo fue instanciado, sino también depende de cómo se llame al método. Es decir, así:
* class CreateAnArrayList {** public **** void ** makeArrayList(T t) {**
List list = new ArrayList();
list.add(t); //un método nada útil, pero se conoce como influye el
} }*
Pareciera que la el método es de tipo y a la vez es un void. Pues escrito así dice que todo lo que está con será el tipo genérico. Esto se podría llamar así: * CreateAnArrayList cal=new CreateAnArrayList(); Auto a2=new Auto(); cal.makeArrayList(a2); *
también podriamos restringirlo para que solo permita números * class CreateAnArrayList {** public **** ** void makeArrayList(T t) {**
List list = new ArrayList();
list.add(t);
} }* y el bloque anterior ya no compilaría, porque a2 es un Auto y no es Number.
Ahora, también serviría como constructor de una clase.* class Radio {** public Radio(T t) { } ** }*