10.2 Uso de genéricos¶
1. Genéricos en métodos¶
Se pueden indicar genéricos en los métodos. Para indicar que el método se puede utilizar con cualquier tipo de objeto y que el método se adaptará a dicho tipo. La sintaxis es: modificadores <Genérico> tipoRetorno nombreMétodo(...)
Ejemplo:
public class ArrayUtils{
public static <T> T getRandom(T a[]){
int i = new Random().nexInt(a.length-1);
return a[i];
}
}
En el ejemplo anterior, se desea obtener un elemento aleatorio de un array. En el ejemplo la letra T entre los signos < y > indica que se va a utilizar un tipo genérico, el hecho de que le siga otra T significa que el tipo que devuelve el método tomarAleatorio será el mismo que el del parámetro a, y se corresponderá al tipo genérico T. Después cuando se invoque a este método, la letra genérica T será traducida por el tipo con el que se invoque al método. Por ejemplo desde este código:
String a[] = {"a", "b", "c", "d", "e", "f"};
String element = ArraysUtils.getRandom(a);
System.out.println(element);
En ese código, como se invoca usando un array de Strings, por lo tanto la letra genérica T será traducida por String. Es decir es como si el código anterior se tradujera como:
public class UtilesArrays {
public static String tomarAleatorio(String a[]){
int i=(int)(Math.random()*a.length);
return a[i];
}
}
2. Genéricos en las clases¶
El método habitual de utilizar genéricos, debido a su mayor potencia, es declarar el genérico en una clase. De esta forma indicados que el código de la clase utiliza tipos genéricos que serán traducidos por el tipo de datos que se desee.
Por ejemplo una clase pensada para extraer elementos aleatorios de un array del tipo que sea se podría declarar así:
public class RandomElement<T> {
private final T element;
public RandomElement(T[] array){
element = array[new Random().nextInt(array.length - 1)];
}
public T getElement(){
return element;
}
}
Los tipos se indican tras el nombre de la clase. De esta forma cuando se creen objetos de clase RandomElement habrá que indicar la clase por la que se traducirá el genérico T. Por ejemplo:
public static void main(String[] args) {
String[] a = {"uno", "dos", "tres", "cuatro"};
RandomElement<String> e = new RandomElement<String>(a);
System.out.println(e.getElement());
}
Obsérvese que tanto al declarar el objeto e hay que concretar el valor del genérico de ahí que la declaración sea RandomElement<String> e
de ese modo ya queda claro la traducción de T
. A partir de ese momento ya todos los métodos de clase y propiedades que utilizaran el genérico T
, ahora utilizarán el tipo String
. En definitiva los genéricos de clase marcan las posiciones para la clase concreta que habrá que especificar en la creación del objeto. Lo bueno es que pueden convivir distintas concreciones de genéricos, podría ser:
RandomElement<String> e1 = new RandomElement<String>(a1);
RandomElement<Integer> e2 = new RandomElement<Integer>(a2);
3. Interfaces con genéricos¶
Al igual que las clases, las interfaces pueden utilizar genéricos en las mismas condiciones que en éstas:
Una clase que implemente la interfaz puede hacerlo así:
De modo que traduce el genérico de la interfaz por un tipo concreto o bien:
De modo que no traduce el genérico y espera a que durante la creación de objetos de esa clase se indique el tipo concreto para el genérico.
4. Uso de varios genéricos¶
Tanto en métodos, como en clases o interfaces, es posible utilizar dos tipos genéricos e incluso más. Para ello se separan por comas dentro del operador diamante. Ejemplo:
public class CompareObjects<T, U>{
protected T object1;
protected U object2;
public CompareObjects(T object1, U object2){
this.object1 = object1;
this.object2 = object2;
}
public boolean sameFirstLetter(){
String letter1 = object1.getClass().getName().substring(1,1);
String letter2 = object2.getClass().getName().substring(1,1);
return letter1.equalsIgnoreCase(letter2);
}
}
Esta clase (que en realidad no es muy útil), construye un objeto a partir de otros dos. El primero de tipo genérico T
, y el segundo de tipo U
. El método sameFirstLetter
devuelve verdadero si el nombre de clase de T
empieza por la misma letra que U
.
Para utilizar esta clase:
public static void main(String[] args){
String s1 = "Hola";
StringBuffer s2 = new StringBuffer("Adiós");
CompareObjects<String, StringBuffer> comp = new CompareObjects<String, StringBuffer>(s1,s2);
System.out.println(comp.sameFirstLetter());
}
Devolverá verdadero porque tanto String
como StringBuffer
empiezan por S
. En el ejemplo T
se convertirá en String y U
en StringBuffer
.