You didn't mention if you were confused about a particular concept, so I'm going to try to just give basic definitions of covariance and invariance.
Covariance preserves type ordering, invariance does not. What this means is that sub-typing is or isn't preserved (inverted in the case of contravariance).
So, if you had the following class
public class A {
public void go() {
System.out.println("A");
}
}
and...
public class B extends A {
@Override
public void go() {
System.out.println("B");
}
}
With covariant typing (e.g. arrays), the function
public static void go(A[] as) {
for (A a : as)
a.go();
}
is perfectly valid used as
A[] as = new A[8];
B[] bs = new B[8];
go(as);
go(bs);
In other words, array types are exposed to the runtime or reified.
With invariant typing (e.g. generics), sub-typing isn't preserved. So, for example, X<B>
would have no type relation to X<A>
other than X
. This is partially a consequence of generic types not being exposed to the runtime or erased.
However, you can still explicitly express covariance and contravariance in Java using extends
and super
respectively. For example with the class
public class X<T extends A> {
private T t_;
public X(T t) {
t_ = t;
}
public void go() {
t_.go();
}
}
The function
public static void go(X<?> x) {
x.go();
}
would be valid used as
X<A> xa = new X<A>(a);
X<B> xb = new X<B>(b);
go(xa);
go(xb);