foo(List<B> list);
bar(List<? extends B> list);
tor(List<? super B> list);
So, what's the difference between these? Well, suppose B
has a subtype C
, and a supertype A
. So the inheritance hierarchy goes A -> B -> C
. You can call foo
with a List<B>
, but not a List<C>
. But you can call bar
with a List<C>
; as well as List<B>
. You can call tor
with a List<A>
and List<B>
. Keep in mind that the contents of the list are not what I am talking about, just the compile-time type of the list.Generics are a bit more complicated than most people assume. Almost anyone who has spent time with Generics has gotten into several puzzling situations that they can't explain, and where the solution isn't obvious.
The question I wonder about is whether it has done more harm than good. This is not a matter of opinion, I think. If we could count up all the hours of lost productivity in wrestling with generics, and count the hours saved by catching bugs and improving the clarity of the code, would the time saved exceed the time lost? My anecdotal evidence is that it is a net benefit. It has rescued the Java world from the perils of type-casting, and I do remember getting many
ClassCastExceptions
in the past. Nowadays they are rare, and I don't spend excessive time on the complexities.