Dê exemplos de funções que demonstram covariância e contravariância nos casos de sobrecarga e substituição em Java? [fechadas]


Respostas:


155

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 covariantListque 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 )


79
O primeiro exemplo de contravariância não funciona em Java. doSomething () na classe Sub é uma sobrecarga, não uma substituição.
Craig P. Motlin,

15
De fato. Java não oferece suporte a argumentos contravariantes em subtipagem. Apenas a covariância para os tipos de retorno do método de interesse (como no primeiro exemplo).
the_dark_destructor

Ótima resposta. A covariância parece lógica para mim. Mas você poderia me apontar um parágrafo no JLS que descreva a contravariância? Por que Sub.doSomething é invocado?
Mikhail

2
Como Craig apontou, não é. Acho que há um conflito entre substituir e sobrecarregar e a SUN escolheu (como sempre) a opção compatível com versões anteriores. Portanto, em Java você não pode usar parâmetros contravariantes ao substituir um método.
Codificado em

1
Seria bom saber por que recebo votos negativos para minha resposta.
Codificado em

48

Co-variância: Iterable e Iterator. Quase sempre faz sentido definir uma co-variante Iterableou 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 nextmétodo, para que possa ser atualizado com segurança T. Mas se você tiver Sextends 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 comparetipo de parâmetro do método, portanto, Tpode 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)

-4

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.


6
Isso não está respondendo à pergunta e é enganoso. Seria inteiramente possível projetar um sistema variante que quebrasse a correção semântica e, portanto, violasse o LSP.
Matt Whipple

este não é o caso de contra variantdizer. super.doSomething("String")não pôde ser substituído por sub.doSomething(Object).
zinking

Não é a questão
OlivierTerrien
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.