Say I have the following class structure
public abstract class MyClass{}
public class MyClassA extends MyClass{}
public class MyClassB extends MyClass{}
How do I create a list of MyClass elements that contains both MyClassA and MyClassB elements without objections?
e.g.
List<MyClass> myList = new LinkedList<MyClassA>();
Depending on your needs you have several options. You can either use plain List<MyClass>
type objects. This allows you to add objects of types MyClassA
and MyClassB
. You can't set a List type object to a variable whose type is List<MyClass>
. This is due to a concept called variance. Three types of variance exist in most programming languages that support generics (parametrized polymofism).
Invariance does not allow one to deviate from a type in either direction. This is the case with generics in Java by default and that's why you can't assign a List<MyClassA>
to a variable of List<MyClass>
.
Imagine the following scenario:
List<MyClassA> listA = new List<MyClassA>();
List<MyClass> list = (List<MyClass>) listA; // Produces a compilation error...
list.add(new MyClassB()); // ... because this would be a big no-no
Covariance allows you to use a the designated parameter type or any subtype thereof. In Java, you use the extends keyword to designate covariance: List<? extends MyClass>
. It is allowed to assign a List type object into such a variable. With covariance, however, you can not add any objects to your generic object instance without an explicit cast. That is you can not do the following:
List<? extends MyClass> list = new LinkedList<MyClassA>();
list.add(new MyClassA());
This is only logical. After all, you can not know exactly what type of a List you are dealing with. It might as well be a List<MyClassB>
.
Contravariance is the opposite of covariance and is designated using the super keyword: List<? super MyClassA>
. It is allowed to assign a List<MyClass>
(but not a List<MyClassB>
) type object into such a variable. With contravariance you can add any subtype of the designated type parameter (lower limit in the type hierarchy). That is, it's allowed to do the following:
List<? super MyClass> list = new LinkedList<MyClass>();
list.add(new ChildA());
You can not, however, know exactly what type of a List the object is. That is you can only direclty assign an object stored in the list into a variable of type Object without an explicit cast:
Object o = list.get(0);
if (o instanceof MyClassA) {
MyClassA mca = (MyClassA) o;
//...
}
I hope this helps to clarify things a bit.