You did not mention whether you were confused in a certain concept, so I will try to just give the basic definitions of covariance and invariance.
Covariance preserves the order of types; invariance does not. This means that subtyping is or is not preserved (inverted in 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) function
public static void go(A[] as) { for (A a : as) a.go(); }
acts great like
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 (for example, generics), subtyping is not preserved. So, for example, X<B> has no relation of type to X<A> , except for X This is partly due to the fact that generic types are not exposed to the runtime or are erased.
However, you can still explicitly express covariance and contravariance in Java using extends and super respectively. For example, with a class
public class X<T extends A> { private T t_; public X(T t) { t_ = t; } public void go() { t_.go(); } }
Function
public static void go(X<?> x) { x.go(); }
will be used as
X<A> xa = new X<A>(a); X<B> xb = new X<B>(b); go(xa); go(xb);