The wildcards do not make a lot of sense when you declare local variables, however they are really important when you declare a parameter for a method.
Imagine you have a method:
int countLegs ( List< ? extends Animal > animals )
{
int retVal = 0;
for ( Animal cur : animals )
{
retVal += cur.countLegs( );
}
return retVal;
}
With this signature you can do this:
List<Dog> dogs = ...;
countLegs( dogs );
List<Cat> cats = ...;
countLegs( cats );
List<Animal> zoo = ...;
countLegs( zoo );
If, however, you declare countLegs like this:
int countLegs ( List< Animal > animals )
Then in the previous example only countLegs( zoo ) would have compiled, because only that call has a correct type.