Q:

Java polymorphism and lists

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>();

A:

Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object. A parent class reference can hold the objects of any of its subclasses. So a collection of a parent can hold the child classes objects. For example:
 List<MyClass> myClassList = new ArrayList<MyClass>();

A:

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
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
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
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.

