Programación

1º DAM/DAW - Curso 2023-2024

User Tools

Site Tools


apuntes:genericos
no way to compare when less than two revisions

Differences

This shows you the differences between two versions of the page.


Next revision
apuntes:genericos [2021/03/10 13:16] – created Santiago Faci
Line 1: Line 1:
 +====== Genéricos ======
 +
 +\section{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:
 +
 +\begin{lstlisting}[language=java]
 +List<String> listaCadenas = new ArrayList<>();
 +\end{lstlisting}
 +
 +Lo que permite que, si cometemos algún error como este, podamos ser avisados en tiempo de compilación:
 +
 +\begin{lstlisting}[language=java]
 +Integer i = listaCadenas.get(0);    // Error de compilación
 +\end{lstlisting}
 +
 +Sin la existencia de los genéricos, la línea anterior sería como esta:
 +
 +\begin{lstlisting}[language=java]
 +Integer i = listaCadenas.get(0);    // Nunca daría error de compilación
 +\end{lstlisting}
 +
 +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 \emph{casting}
 +necesarios para que el código pueda ejecutarse sin problemas.
 +
 +\subsection{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:
 +
 +\begin{lstlisting}[language=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());
 +    }
 +}
 +\end{lstlisting}
 +
 +De esa forma, una definición genérica como la anterior, puede emplearse con distintos tipos de datos:
 +
 +\begin{lstlisting}[language=java]
 +Grupo<Alumno> grupoAlumnos = new Grupo<>(20);
 +. . .
 +Grupo<Profesor> grupoProfesores = new Grupo<>(10);
 +. . .
 +\end{lstlisting}
 +
 +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:
 +
 +\begin{itemize}
 +    \item \verb E  para definir un elemento de una colección
 +    \item \verb K  para definir una clave en un mapa
 +    \item \verb V  para definir un valor en un mapa
 +    \item \verb T , \verb U  para definir tipos de datos (clases)
 +\end{itemize}
 +
 +\subsection{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:
 +
 +\begin{lstlisting}[language=java]
 +public class Utils {
 +    public <T> T getUltimoElemento(ArrayList<T> lista) {
 +        return lista.get(lista.size() - 1);
 +    }
 +}
 +\end{lstlisting}
 +
 +De esa manera, se podrá invocar al método \verb getUltimoElemento  con cualquier \verb ArrayList , independientemente del tipo que sea.
 +
 +\subsection{El comodín ?}
 +
 +También se puede utilizar el caracter comodín \verb ?  para indicar que se acepta cualquier tipo, definiendo, por ejemplo, como parámetro un
 +\verb ArrayList  de la siguiente forma:
 +
 +\begin{lstlisting}[language=java]
 +. . .
 +public void metodo(ArrayList<?> elementos) {
 +    . . .
 +    . . .
 +}
 +. . .
 +\end{lstlisting}
 +
 +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:
 +
 +\begin{lstlisting}[language=java]
 +. . .
 +public void Zoo {
 +    . . .
 +    public void anadirAnimales(ArrayList<? extends Animal> animales) {
 +        . . .
 +        . . .
 +    }
 +    . . .
 +}
 +. . .
 +\end{lstlisting}
 +
 +Y para aceptar cualquier clase que esté por encima de una determinada clase:
 +
 +\begin{lstlisting}[language=java]
 +. . .
 +public void Zoo {
 +    . . .
 +    public void anadirAnimales(ArrayList<? super Animal> animales) {
 +        . . .
 +        . . .
 +    }
 +    . . .
 +}
 +. . .
 +\end{lstlisting}
 +
 +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:
 +
 +\begin{lstlisting}[language=java]
 +public class Animal {
 +. . .
 +}
 +\end{lstlisting}
 +
 +\begin{lstlisting}[language=java]
 +public class Leon extends Animal {
 +. . .
 +}
 +\end{lstlisting}
 +
 +\begin{lstlisting}[language=java]
 +public class Zoo {
 +    . . .
 +    public void anadirAnimales(ArrayList<Animal> animales) {
 +        . . .
 +        . . .
 +    }
 +    . . .
 +}
 +\end{lstlisting}
 +
 +Las siguiente invocaciones producirían un error de compilación por incompatibilidad de tipos:
 +
 +\begin{lstlisting}[language=java]
 +. . .
 +Zoo zoo = new Zoo(. . .);
 +. . .
 +ArrayList<Leon> leones = new ArrayList<>();
 +zoo.anadirAnimales(leones);                 // Error de compilación
 +. . .
 +\end{lstlisting}
 +
 +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 \verb ? , podremos hacer referencia a la clase genérica a lo largo de la implementación del método o clase.
 +
 +\begin{lstlisting}[language=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());
 +    }
 +}
 +\end{lstlisting}
 +
 +\begin{lstlisting}[language=java]
 +. . .
 +public <T extends Animal> T getAnimal(ArrayList<T extends Animal> animales) {
 +    . . .
 +    // Podemos hacer rerencia a T como clase genérica
 +    . . .
 +    return T;
 +}
 +. . .
 +\end{lstlisting}
  
apuntes/genericos.txt · Last modified: 2023/05/28 12:04 by Santiago Faci