PECS - Tensho97/Aprende-a-Aprender GitHub Wiki
Los tipos genéricos permiten establecer restricciones a nivel de tipo, haciendo que cierta clases, interfaces o métodos acepten únicamente los tipos estipulados.
Cuando se trata con jerarquías los genéricos son invariantes, esto es, dado un Tipo1 subtipo de otro Tipo2, para los genéricos, List Tipo1 no es considerado como subtipo ni un supertipo de List Tipo2 excepto en el caso trivial de que A y B sean idénticos.
El problema como hemos visto , los tipos genéricos en Java son invariantes.
Pongamos por ejemplo un método que copia valores de una colección a otra:
public class Util<T> {
public void copia(final Collection<T> desde, final Collection<T> hasta){
for(final T object : desde){
hasta.add(object);
}
}
Este método funcionará correctamente siempre que se utilice con el mismo tipo con el que se instancia la clase Util.
En el momento en el que queramos realizar esta acción con una clase descendiente de la clase origen resultará en un error en tiempo de compilación del tipo:
The method copia(Collection<Number>, Collection<Number>) in the type Util<Number> is not applicable for the arguments (List<Number>, List<Integer>)
Java ofrece la posibilidad de utilizar el tipo comodín (?) que nos ayuda en el trato con los tipos genéricos y que significa, ni más ni menos, «cualquier tipo de objeto».
Este símbolo puede utilizarse tanto con la palabra extends como con la palabra super, para limitar el rango de objetos aceptados a un subtipo concreto, o a un supertipo respectivamente.
PECS es el acrónimo de «Producer-Extends Consumer-Super» y trata sobre los tipos genéricos y a los comodínes.
Si el parámetro que estamos utilizando en la operación actúa como un productor deberá utilizarse extends (Collection<? extends T>), mientras que si el parámetro actúa como un consumidor deberá utilizarse super (Collection<? super T>).
Aplicado al código anterior quedaría como:
public class Util<T> {
public void copia(final Collection<? extends T> desde,final Collection<T> hasta){
for(final T object : desde){
hasta.add(object);
}
}
De esta manera se podrá utilizar el método «copia» con cualquier clase que extienda de la clase T establecida en el momento de la instanciación de la clase Util.
El operador comodín establece, básicamente, el tipo del Collection «desde» como «algún subtipo de T».
Este problema puede darse en el sentido contrario, quizás lo que se desea es que el objeto en el que se realice la copia sea de cualquier supertipo de la clase T, en este sentido basta con cambiar las notaciones para que el tipo del Collection «hasta» sea «algún supertipo de T».
En el siguiente ejemplo vemos como quedaría dicha notación:
public class Util<T> {
public void copia(final Collection<T> desde, final Collection<? super T> hasta){
for(final T object : desde){
hasta.add(object);
}
}
Autor: Julián