Por favor, mostre um bom exemplo de covariância e contravariância em Java.
fonte
Por favor, mostre um bom exemplo de covariância e contravariância em Java.
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
ouIterator
.Iterator<? extends T>
pode ser usado da mesma forma queIterator<T>
- o único lugar onde o parâmetro de tipo aparece é o tipo de retorno donext
método, para que possa ser atualizado com segurançaT
. Mas se você tiverS
extendsT
, também pode atribuirIterator<S>
a uma variável do tipoIterator<? extends T>
. Por exemplo, se você estiver definindo um método find:você não será capaz de chamá-lo com
List<Integer>
e5
, então é melhor definido comoContra-variância: Comparador. Quase sempre faz sentido usar
Comparator<? super T>
, porque pode ser usado exatamente comoComparator<T>
. O parâmetro de tipo aparece apenas como ocompare
tipo de parâmetro do método, portanto,T
pode ser passado com segurança para ele. Por exemplo, se você tem umDateComparator implements Comparator<java.util.Date> { ... }
e deseja classificar umList<java.sql.Date>
com esse comparador (java.sql.Date
é uma subclasse dejava.util.Date
), você pode fazer com:mas não com
fonte
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.
fonte
contra variant
dizer.super.doSomething("String")
não pôde ser substituído porsub.doSomething(Object)
.