Respostas:
Covariância:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething é covariante porque retorna uma subclasse do tipo de retorno de Super # getSomething (mas cumpre o contrato de Super.getSomething ())
Contravariância
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething é contravariante porque leva um parâmetro de uma superclasse do parâmetro de Super # doSomething (mas, novamente, cumpre o contrato de Super # doSomething)
Aviso: este exemplo não funciona em Java. O compilador Java iria sobrecarregar e não substituir o método doSomething () -. Outras línguas suportam esse estilo de contravariância.
Genéricos
Isso também é possível para Genéricos:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Agora você pode acessar todos os métodos covariantList
que não levam um parâmetro genérico (já que deve ser algo "extends Object"), mas getters funcionarão bem (já que o objeto retornado sempre será do tipo "Object")
O oposto é verdadeiro para contravariantList
: Você pode acessar todos os métodos com parâmetros genéricos (você sabe que deve ser uma superclasse de "String", então você sempre pode passar um), mas nenhum getter (O tipo retornado pode ser de qualquer outro supertipo de String )
Co-variância: Iterable e Iterator. Quase sempre faz sentido definir uma co-variante Iterable
ou Iterator
. Iterator<? extends T>
pode ser usado da mesma forma que Iterator<T>
- o único lugar onde o parâmetro de tipo aparece é o tipo de retorno do next
método, para que possa ser atualizado com segurança T
. Mas se você tiver S
extends T
, também pode atribuir Iterator<S>
a uma variável do tipo Iterator<? extends T>
. Por exemplo, se você estiver definindo um método find:
boolean find(Iterable<Object> where, Object what)
você não será capaz de chamá-lo com List<Integer>
e 5
, então é melhor definido como
boolean find(Iterable<?> where, Object what)
Contra-variância: Comparador. Quase sempre faz sentido usar Comparator<? super T>
, porque pode ser usado exatamente como Comparator<T>
. O parâmetro de tipo aparece apenas como o compare
tipo de parâmetro do método, portanto, T
pode ser passado com segurança para ele. Por exemplo, se você tem um DateComparator implements Comparator<java.util.Date> { ... }
e deseja classificar um List<java.sql.Date>
com esse comparador ( java.sql.Date
é uma subclasse de java.util.Date
), você pode fazer com:
<T> void sort(List<T> what, Comparator<? super T> how)
mas não com
<T> void sort(List<T> what, Comparator<T> how)
Observe o princípio de substituição de Liskov . Com efeito, se a classe B estende a classe A, então você deve ser capaz de usar um B sempre que um A for necessário.
contra variant
dizer. super.doSomething("String")
não pôde ser substituído por sub.doSomething(Object)
.