2010-03-23 26 views

Respuesta

119

covarianza:

class Super { 
    Object getSomething(){} 
} 
class Sub extends Super { 
    String getSomething() {} 
} 

Sub # getSomething es covariante porque devuelve una subclase del tipo de retorno de Súper # getSomething (pero quedan satisfechas por el contrato de Super.getSomething())

contravarianza

class Super{ 
    void doSomething(String parameter) 
} 
class Sub extends Super{ 
    void doSomething(Object parameter) 
} 

Sub # doSomething es contrava riant porque toma un parámetro de una superclase del parámetro de Super # doSomething (pero, de nuevo, cumple el contrato de Super # doSomething)

Aviso: este ejemplo no funciona en Java. El compilador de Java sobrecargaría y no anularía el método doSomething(). Otros idiomas admiten este estilo de contravarianza.

Genéricos

Esto también es posible para los genéricos:

List<String> aList... 
List<? extends Object> covariantList = aList; 
List<? super String> contravariantList = aList; 

Ahora puede acceder a todos los métodos de covariantList que no tienen un parámetro genérico (como debe ser algo "extends Object"), pero los getters funcionarán bien (ya que el objeto devuelto siempre será del tipo "Object")

Lo opuesto es cierto para contravariantList: puede acceder a todos los métodos con parámetros genéricos (usted sabe que debe ser una superclase de "Cadena", por lo que siempre puede pasar uno), pero no getters (El tipo devuelto puede ser de cualquier otro supertipo de Cadena)

+68

El primer ejemplo de contravariancia no funciona en Java. doSomething() en la Sub clase es una sobrecarga, no una anulación. –

+13

De hecho. Java no admite argumentos contravariantes en la subtipificación. Solo covarianza para lo que concierne a los tipos de devolución de método (como en el primer ejemplo). –

+0

Gran respuesta. La covarianza me parece lógica. ¿Pero podría indicarme un párrafo en JLS que describa la contradicción? ¿Por qué se invoca Sub.doSomething? – Mikhail

1

Mire el Liskov substitution principle. En efecto, si la clase B extiende la clase A, entonces usted debería poder usar una B siempre que se requiera una A.

+2

Este no responde la pregunta y es engañoso. Sería completamente posible diseñar un sistema variante que rompa la corrección semántica y, por lo tanto, viole el LSP. –

+0

, este no es el caso para 'contra variant' decir. 'super.doSomething (" String ")' no se pudo reemplazar por 'sub.doSomething (Object)'. – zinking

38

Co-varianza: Iterable e Iterador. Casi siempre tiene sentido definir una covariante Iterable o Iterator. Iterator<? extends T> se puede usar igual que Iterator<T> - el único lugar donde aparece el parámetro de tipo es el tipo de devolución del método next, por lo que se puede actualizar de manera segura a T. Pero si tiene S extiende T, también puede asignar Iterator<S> a una variable del tipo Iterator<? extends T>. Por ejemplo, si se está definiendo un método de búsqueda:

boolean find(Iterable<Object> where, Object what) 

usted no será capaz de llamar con List<Integer> y 5, así que es mejor definido como

boolean find(Iterable<?> where, Object what) 

Contra-varianza: Comparador. Casi siempre tiene sentido usar Comparator<? super T>, ya que se puede usar como Comparator<T>. El parámetro tipo solo aparece como el tipo de parámetro del método compare, por lo que T se puede pasar con seguridad.Por ejemplo, si usted tiene un DateComparator implements Comparator<java.util.Date> { ... } y desea ordenar un List<java.sql.Date> con que comparador (java.sql.Date es una subclase de java.util.Date), se puede hacer con:

<T> void sort(List<T> what, Comparator<? super T> how) 

pero no con

<T> void sort(List<T> what, Comparator<T> how) 
Cuestiones relacionadas