Generics (II) – Clases genéricas

En la anterior entrada hablamos de qué eran las Generics y por qué eran útiles. En ésta veremos cómo se definen clases genéricas para su posterior uso.

Clases genéricas

Para ver cómo definir las clases, vamos a partir de un ejemplo sencillo. Supongamos que en nuestra aplicación tenemos la necesidad de trabajar con pares. En algunos puntos necesitaremos usar pares de manzanas y en otros puntos pares de piedras.

Dado que el cometido de estos pares de elementos dentro de nuestra aplicación va a ser similar, hemos decidido implementar una clase que represente un par de cosas, en lugar de dos clases, la del par de manzanas y la del par de tornillos. Además queremos que sea una clase genérica por las ventajas que nos aportan.

Por tanto, una posible implementación de la clase podría ser la siguiente:

public class Pair<T> {
  private T first;
  private T second;
 
  public T getFirst() {
    return first;
  }
 
  public T getSecond() {
    return second;
  }
 
  public void setFirst(T first) {
    this.first = first;
  }
 
  public void setSecond(T second) {
    this.second = second;
  }
}

NOTA: Como convenio se suele utilizar una letra mayúscula para indicar una clase genérica, siendo T y E las más usadas.

Hemos definido una clase de un tipo genérico. Se puede leer como esto es una clase que representa un par de objetos de la clase T, y dejamos al criterio del usuario de la clase de qué tipo quiere o no usarla.

Definir una clase genérica no tiene más misterio. Se puede hacer todo lo enrevesado que queramos, pero en esencia toda definición se reduce a esto. El punto clave es la T. Le estamos indicando al compilador que a la hora de usar la clase Pair, la clase que pongamos en lugar de la T situada entre los símbolos < y > se sustituirá por todas las T que aparezcan en el resto de la clase.

A continuación una serie de ejemplos con comportamientos que funcionan y comportamientos que dan error:

// ...
Pair<Integer> intPair = new Pair<Integer>();
intPair.setFirst(7); // OK
intPair.setSecond("5"); // ERROR
//...
Pair objPair = new Pair();
objPair.setFirst(7); // OK
objPair.setSecond("5"); // OK
//...

No parece que tengan mucho misterio, ¿no?. Únicamente puntualizar que, si no especificamos las clases, el objeto creado usará Object como clase para el tipo genérico, y no será necesario especificarlo.

Múltiples clases

Si por ejemplo, quisiésemos que el par de elementos fuesen de dos tipos, independietes el uno del otro, podríamos redefinir la clase Pair como:

public class Pair<T, E> {
  private T first;
  private E second;
 
  public T getFirst() {
    return first;
  }
 
  public E getSecond() {
    return second;
  }
 
  public void setFirst(T first) {
    this.first = first;
  }
 
  public void setSecond(E second) {
    this.second = second;
  }
}

Aquí estamos definiendo dos tipos, (T y E). De esta forma, la clase que pongamos en lugar de T se sustituirá en todos los T, y lo mismo con la clase que pongamos en lugar de E. Ejemplo:

// ...
Pair<Integer, String> intPair = new Pair<Integer, String>();
intPair.setFirst(7); // OK
intPair.setSecond("5"); // OK
//...
Pair objPair = new Pair();
objPair.setFirst(7); // OK
objPair.setSecond("5"); // OK
//...

Para finalizar

Poco más hay que contar sobre cómo declarar una clase para que se pueda usar de forma genérica. Así que para finalizar, voy a hacer una pregunta capciosa. Dada la siguiente jerarquía:

Jerarquía de clases

¿cuál de los siguientes ejemplos daría error?

// EJEMPLO 1
Collection<Apple> apples = new ArrayList<Apple>();
Collection<Stones> stones = apples;
// ...
// EJEMPLO 2
Collection<Apple> apples = new ArrayList<Apple>(); 
Collection<Thing> things = apples;
// ...

En efecto, el primer ejemplo da un error, ya que una colección de piedras no es lo mismo que una de manzanas (afortunadamente para los que comemos fruta).

Lo malo es que el segundo ejemplo también es incorrecto. Para Java, una colección de manzanas no es equivalente a una colección de cosas. La respuesta y la solución las veremos en el siguiente post dedicado a las clases comodín (las «Wildcards»). ¡Sed felices!.

4 opiniones en “Generics (II) – Clases genéricas”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *