====== Genéricos ======

===== Genéricos =====

El uso más conocido de los genéricos lo vimos en el tema anterior, con las colecciones, ya que éstas están definidas como clases genéricas,
lo que permite que podamos definir estructuras de la siguiente forma:

<code java>
List<String> listaCadenas = new ArrayList<>();
</code>

Lo que permite que, si cometemos algún error como este, podamos ser avisados en tiempo de compilación:

<code java>
Integer i = listaCadenas.get(0);    // Error de compilación
</code>

Sin la existencia de los genéricos, la línea anterior sería como esta:

<code java>
Integer i = listaCadenas.get(0);    // Nunca daría error de compilación
</code>

Y nunca produciría ningún error de compilación en caso de que estuvieramos cometiendo algún error. A este respecto conviene saber que los
genéricos sólo existen a nivel de compilación, para evitar este tipo de fallos. En el código generado se añaden todos los ''casting''
necesarios para que el código pueda ejecutarse sin problemas.

==== Definición de una clase genérica ====

El siguiente fragmento de código se correspondería con la definición de una clase genérica:

<code java>
public class Grupo<E> {
    private List<E> participantes;
    private int limite;

    public Grupo(int limite) {
        this.limite = limite;
        participantes = new ArrayList<>();
    }

    public void anadirParticipante(E participante) {
        if (participantes.size() == limite)
            return;

        participantes.add(participante);
    }

    public E getParticipante(int posicion) {
        return participantes.get(posicion);
    }

    public int getNumeroParticipantes() {
        return participantes.size();
    }

    public int getEspacioLibre() {
        return (limite - participantes.size());
    }
}
</code>

De esa forma, una definición genérica como la anterior, puede emplearse con distintos tipos de datos:

<code java>
Grupo<Alumno> grupoAlumnos = new Grupo<>(20);
. . .
Grupo<Profesor> grupoProfesores = new Grupo<>(10);
. . .
</code>

Como has podido ver en la definición, al tipo genérico va entre los caracteres < y > definido con una sola letra mayúscula, y una vez
definida dicha letra en la definición de la clase, se emplea siempre que se quiera hacer referencia al tipo para el que se define dicha
clase genérica. A este respecto se suelen utilizar diferentes letras para diferentes situaciones:

  * ''E'' para definir un elemento de una colección
  * ''K'' para definir una clave en un mapa
  * ''V'' para definir un valor en un mapa
  * ''T'', ''U''  para definir tipos de datos (clases)

==== Métodos genéricos ====

Así como se pueden definir clases genéricas, también podemos definir métodos genéricos dentro de clases que no lo sean:

<code java>
public class Utils {
    public <T> T getUltimoElemento(ArrayList<T> lista) {
        return lista.get(lista.size() - 1);
    }
}
</code>

De esa manera, se podrá invocar al método ''getUltimoElemento'' con cualquier ''ArrayList'', independientemente del tipo que sea.

==== El comodín ? ====

También se puede utilizar el caracter comodín ''?'' para indicar que se acepta cualquier tipo, definiendo, por ejemplo, como parámetro un
\verb ArrayList  de la siguiente forma:

<code java>
. . .
public void metodo(ArrayList<?> elementos) {
    . . .
    . . .
}
. . .
</code>

Para este caso también podemos aprovecharnos de las características de la herencia para la definición de métodos genéricos, pudiendo definir el uso de la
clase genérica como clase derivada o como superclase de una, haciendo uso de este comodín.

Para aceptar cualquier clase que extienda de una determinada clase:

<code java>
. . .
public void Zoo {
    . . .
    public void anadirAnimales(ArrayList<? extends Animal> animales) {
        . . .
        . . .
    }
    . . .
}
. . .
</code>

Y para aceptar cualquier clase que esté por encima de una determinada clase:

<code java>
. . .
public void Zoo {
    . . .
    public void anadirAnimales(ArrayList<? super Animal> animales) {
        . . .
        . . .
    }
    . . .
}
. . .
</code>

De esa manera, podremos invocar al método \verb anadir  de múltiples formas, siempre y cuando pasemos como parámetro \verb ArrayList un tipo
que corresponda con la expresión del comodín que acompaña a la declaración de ese \verb ArrayList . Para entender esta parte hay que tener en cuenta el siguiente caso, y es que dadas las
siguientes clases:

<code java>
public class Animal {
. . .
}
</code>

<code java>
public class Leon extends Animal {
. . .
}
</code>

<code java>
public class Zoo {
    . . .
    public void anadirAnimales(ArrayList<Animal> animales) {
        . . .
        . . .
    }
    . . .
}
</code>

Las siguiente invocaciones producirían un error de compilación por incompatibilidad de tipos:

<code java>
. . .
Zoo zoo = new Zoo(. . .);
. . .
ArrayList<Leon> leones = new ArrayList<>();
zoo.anadirAnimales(leones);                 // Error de compilación
. . .
</code>

Es por eso que tenemos que usar lo que se conoce como los comodínes.

Para el caso de las clases genéricas y métodos genéricos también podemos jugar con las características de herencia, utilizando las mismas
expresiones que podemos usar con el comodín, pero con el caracter que empleemos para referirnos a la clase genérica. A diferencia del uso
del comodín ''?'', podremos hacer referencia a la clase genérica a lo largo de la implementación del método o clase.

<code java>
public class Grupo<E extends Persona> {
    private List<E> participantes;
    private int limite;

    public Grupo(int limite) {
        this.limite = limite;
        participantes = new ArrayList<>();
    }

    public void anadirParticipante(E participante) {
        if (participantes.size() == limite)
            return;

        participantes.add(participante);
    }

    public E getParticipante(int posicion) {
        return participantes.get(posicion);
    }

    public int getNumeroParticipantes() {
        return participantes.size();
    }

    public int getEspacioLibre() {
        return (limite - participantes.size());
    }
}
</code>

<code java>
. . .
public <T extends Animal> T getAnimal(ArrayList<T extends Animal> animales) {
    . . .
    // Podemos hacer rerencia a T como clase genérica
    . . .
    return T;
}
. . .
</code>

----

(c) 2019-{{date>%Y}} Santiago Faci
