1) Why does line 1 successfully compile?
You basically define the list to contain elements of any type that extends (or is) Super
, i.e. the compiler knows that every element in that list should at least have the properties of Super
.
Since Sub
is a subclass of Super
and thus any list containing only Sub
elements also meets the requirement of all elements being instances of Super
, List<? extends Super> list = new ArrayList<Sub>();
is correct.
2) Is line 1 a good practice in declaring a List (or other collections)?
As a local variable that depends on personal style, IMHO. When declaring parameters that way (or instance/static variables) that's often not only good style but also needed.
Consider a method that iterates over a collection of numbers and returns a sum.
You could declare the parameter to be Collection<Number>
but then you couldn't pass a Collection<Integer>
without a nasty cast. If the parameter is declared as Collection<? extends Number>
you can pass Collection<Integer>
.
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
The reason is that the compiler doesn't know the exact type of the elements in the list. Is it a list of Super
or a list of Sub
?
As an example take List<? extends Number> list
. You don't know whether you have a List<Number>
, a List<Double>
or a List<Integer>
and thus can't tell whether list.add( new Integer(1) );
would be ok or not. That's how the compiler sees it.
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
I'd have to guess here but adding null
to a list would be ok since no matter what type the actual list declares, you can always cast null
to that type.