Taken directly from my own notes that I used to prepare for SCJP 5.0:
1. The type of the variable declaration must match the type you pass to the actual object type.
List<Foo> f = new ArrayList<Foo>(); //OK
List<Foo> j = new ArrayList<Bar>(); //NOT OK even if Bar is-a Foo
Parent[] myArray = new Child[4]; //OK when Child is-a Parent
2. Method myMethod(List<Animal> list) can not accept a List<Dog>, List<Cat> ONLY List<Animal>.
List<Animal> list = new ArrayList<Animal>();
list.add(new Dog()); //OK when Dog is-a Animal
list.add(new Cat()); //OK when cat is-a Animal
3. Method myMethod(List<? extends Animal> list) CAN accept a List<Dog> ONLY if you do NOT ADD to the list
List<Dog> listDog = new ArrayList<Dog>();
myMethod(listDog); //OK only if myMethod doesn't add to the list.
4. <? extends ...> works for both classes and interfaces, you use extends for both.
5. <? super Dog> - Accept anything of type Dog or the SUPERTYPE of Dog, then you can add to this list.
6. <?> means you can pass ANY type (Dog, Cat, Car, Bag) but you CAN NOT add to the collection
<Object> means you can ONLY pass an Object type, but you CAN add to this collection
7. <?> = <? extends Object>, in both cases you can't add to the collection anyway
8. List<?> foo = new ArrayList<? extends Animal>(); // NO GOOD!!! WILL NOT COMPILE, can't use wildcard on right hand side
HTH