java - descargar - ¿Covarianza, invariancia y contravarianza explicadas en inglés simple?
java offline (2)
Hoy, leo algunos artículos sobre Covarianza, Contravariancia (e Invarianza) en Java. Leí el artículo de Wikipedia en inglés y alemán, y algunas otras publicaciones de blog y artículos de IBM.
Pero todavía estoy un poco confundido acerca de qué se trata exactamente. Algunos dicen que se trata de la relación entre tipos y subtipos, algunos dicen que se trata de la conversión de tipos y algunos dicen que se usa para decidir si un método se anula o se sobrecarga.
Así que estoy buscando una explicación fácil en inglés simple, que muestre a un principiante lo que es la covarianza y la contradicción (y la invarianza). Un punto más para un ejemplo fácil.
Algunos dicen que se trata de relaciones entre tipos y subtipos, otros dicen que se trata de la conversión de tipos y otros dicen que se usa para decidir si un método se sobreescribe o se sobrecarga.
Todas las anteriores.
En el fondo, estos términos describen cómo la relación de subtipo se ve afectada por las transformaciones de tipo. Es decir, si A
y B
son tipos, f
es una transformación de tipo, y ≤ la relación de subtipo (es decir, A ≤ B
significa que A
es un subtipo de B
), tenemos
-
f
es covariante siA ≤ B
implica quef(A) ≤ f(B)
-
f
es contravariante siA ≤ B
implica quef(B) ≤ f(A)
-
f
es invariante si ninguno de los anteriores sostiene
Consideremos un ejemplo. Deje f(A) = List<A>
donde List
es declarado por
class List<T> { ... }
¿Es f covariante, contravariante o invariante? Covariante significaría que List<String>
es un subtipo de List<Object>
, contravariante que List<Object>
es un subtipo de List<String>
e invariante que tampoco es un subtipo del otro, es decir, List<String>
y List<Object>
son tipos inconvertibles. En Java, esto último es cierto, decimos (de manera un tanto informal) que los genéricos son invariables.
Otro ejemplo. Deje f (A) = A[]
. ¿Es f covariante, contravariante o invariante? Es decir, ¿String [] es un subtipo de Object [], Object [] un subtipo de String [], o no es un subtipo de la otra? (Respuesta: en Java, las matrices son covariantes)
Esto todavía era bastante abstracto. Para hacerlo más concreto, veamos qué operaciones en Java se definen en términos de la relación de subtipo. El ejemplo más simple es la asignación. La declaración
x = y;
compilará solo si typeof (y) ≤ typeof (x). Es decir, acabamos de enterarnos de que las declaraciones
ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();
no se compilará en Java, pero
Object[] objects = new String[1];
será.
Otro ejemplo donde importa la relación de subtipo es una expresión de invocación de método:
result = method(a);
Hablando informalmente, esta afirmación se evalúa asignando el valor de a al primer parámetro del método, luego ejecutando el cuerpo del método y luego asignando los métodos valor devuelto al result
. Al igual que la asignación simple en el último ejemplo, el "lado derecho" debe ser un subtipo del "lado izquierdo", es decir, esta afirmación solo puede ser válida si typeof (a) ≤ typeof (parámetro (método)) y returntype ( método) ≤ typeof (resultado). Es decir, si el método es declarado por:
Number[] method(ArrayList<Number> list) { ... }
ninguna de las siguientes expresiones se compilará:
Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());
pero
Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());
será.
Otro ejemplo donde la subtipificación importa es primordial. Considerar:
Super sup = new Sub();
Number n = sup.method(1);
dónde
class Super {
Number method(Number n) { ... }
}
class Sub extends Super {
@Override
Number method(Number n);
}
Informalmente, el tiempo de ejecución reescribirá esto para:
class Super {
Number method(Number n) {
if (this instanceof Sub) {
return ((Sub) this).method(n); // *
} else {
...
}
}
}
Para la línea marcada a compilar, el parámetro de método del método de anulación debe ser un supertipo del parámetro de método del método reemplazado, y el tipo de retorno es un subtipo del método reemplazado. Formalmente hablando, f (A) = parametertype (método asdeclaredin (A)) debe al menos ser contravariante, y si f (A) = returntype (método asdeclaredin (A)) debe al menos ser covariante.
Tenga en cuenta el "al menos" de arriba. Esos son los requisitos mínimos que exigirá cualquier lenguaje de programación orientado a objetos seguro de tipo estático razonable, pero un lenguaje de programación puede elegir ser más estricto. En el caso de Java 1.4, los tipos de parámetros y los tipos de retorno de método deben ser idénticos (excepto el borrado de tipo) cuando se anulan los métodos, es decir, tipo de parámetro (método asdeclaredin (A)) = tipoparámetro (método asdeclaredin (B)). Desde Java 1.5, se permiten los tipos de retorno covariantes cuando se anula, es decir, los siguientes se compilarán en Java 1.5, pero no en Java 1.4:
class Collection {
Iterator iterator() { ... }
}
class List extends Collection {
@Override
ListIterator iterator() { ... }
}
Espero haber cubierto todo, o mejor dicho, haber arañado la superficie. Aún así, espero que ayude a comprender el concepto abstracto, pero importante, de la varianza de tipo.
Tomando el sistema tipo java, y luego las clases:
Cualquier objeto de algún tipo T puede ser sustituido por un objeto de subtipo de T.
VARIEDAD DE TIPO - LOS MÉTODOS DE CLASE TIENEN LAS SIGUIENTES CONSECUENCIAS
class A {
public S f(U u) { ... }
}
class B extends A {
@Override
public T f(V v) { ... }
}
B b = new B();
t = b.f(v);
A a = ...; // Might have type B
s = a.f(u); // and then do V v = u;
Puede observarse que:
- El T debe ser el subtipo S ( covariante, ya que B es el subtipo de A ).
- El V debe ser un supertipo de U ( contravariante , como contra dirección de herencia).
Ahora co-y contra-se refieren a que B es el subtipo de A. Los siguientes typings más fuertes pueden ser introducidos con conocimiento más específico. En el subtipo
La covarianza (disponible en Java) es útil, para decir que uno arroja un resultado más específico en el subtipo; especialmente visto cuando A = T y B = S. La contradicción dice que estás preparado para manejar un argumento más general.