Formal definition
摘自Covariance_and_contravariance_(computer_science):
Within the type system of a programming language, a
typing rule
or atype constructor
is:
- covariant if it preserves the rdering of types (≤), which orders types from more specific to more generic;
- contravariant if it reverses this ordering;
- bivariant if both of these apply (i.e., both
I<A>
≤I<B>
andI<B>
≤I<A>
at the same time);[1] - variant if covariant, contravariant or bivariant;
- invariant or nonvariant if not variant.
协变
首先考虑数组类型构造器: 从Animal类型,可以得到Animal[]
(“animal数组”)。 是否可以把它当作
- 协变:a
Cat[]
is anAnimal[]
- 逆变:an
Animal[]
is aCat[]
- 不变:an
Animal[]
is not aCat[]
and aCat[]
is not anAnimal[]
Java中的协变数组
// a 是单元素的 String 数组
String[] a = new String[1];
// b 是 Object 的数组
Object[] b = a;
// 向 b 中赋一个整数。如果 b 确实是 Object 的数组,这是可能的;然而它其实是个 String 的数组,因此会发生 java.lang.ArrayStoreException
b[0] = 1;
OOP中的继承
当一个子类重写一个超类的方法时,编译器必须检查重写方法是否具有正确的类型。虽然一些语言要求类型必须与超类相同,但允许重写方法有一个“更好的”类型也是类型安全的。对于大部分的方法子类化规则来说,这要求返回值的类型必须更具体,也就是
协变
,而且接受更宽泛的参数类型,也就是逆变
。
以下讨论基于Java语法,抽象类C
如下:
jshell> abstract class C {
...> abstract C foo(C c);
...> }
| replaced class C
返回值的协变
在允许协变返回值的语言中, 子类可以重写foo
方法返回一个更具体的类型:
jshell> class D extends C {
...> @Override
...> D foo(C c) {return this;}
...> }
| created class D
方法参数的协变与逆变
允许参数协变、逆变的面向对象语言并不多——Java会把它当成一个函数重载:
协变:
jshell> class E extends C {
...> @Override
...> C foo(E e) {return this;}
...> }
| Error:
| E is not abstract and does not override abstract method foo(C) in C
| class E extends C {
| ^------------------...
| Error:
| method does not override or implement a method from a supertype
| @Override
| ^-------^
逆变:
jshell> class F extends C {
...> @Override
...> C foo(Object o) {return this;}
...> }
| Error:
| F is not abstract and does not override abstract method foo(C) in C
| class F extends C {
| ^------------------...
| Error:
| method does not override or implement a method from a supertype
| @Override
| ^-------^
Summary of variance and inheritance
语言 | Parameter type | Return type |
---|---|---|
C++ (自1998年), Java (自J2SE 5.0), D | 不变 | 协变 |
C# | 不变 | 不变 |