Tobias Bachert posted a truly impressive
code snippet in a discussion about how to achieve a kind of
polymorphism where the signature alone is not enough to choose the method you need. His code made me realize I needed to learn more about generics, so I've been poring over them for the last few days. Most of the time, Cay Horstmann's excellent "Core Java" tells me the answers to my questions, but I was having a really hard time understanding what wildcards are for. Turning instead to "Head First
Java," I read Sierra and Bates's take on generics, particularly their explanation as to why something like
<T extends Animal> is necessary to polymorphism when passing what they call "generified" parameters. That all made pretty good sense. But, towards the end of their discussion, they introduce wildcards in a way that, to me, seemed to suggest they were duplicative of what the
extends paradigm does.
Lo and behold, they actually then say this:
Sierra & Bates wrote:
This:
public <T extends Animal> void takeThing(ArrayList<T> list)
Does the same thing as this:
public void takeThing(ArrayList<? extends Animal> list)
That seemed like a pretty direct confirmation of what I was thinking. They go on to explain that the wildcard version is the one to use when you don't want to use "T" again. For example, they suggest
not using wildcards in a case like this:
Because that gets you out of the somewhat clunkier alternative:
But I am thinking there's also the benefit of not being
able to use T again. For example, consider this code:
Having access to "T" at Line 27 makes it possible for this code to sneak an Animal into an ArrayList that should only have Dog objects in it. This results in a ClassCastException at Line 13, when iterating over the ArrayList attempts to assign an Animal (that is not a Dog) to
dog.
If I rewrite the offending routine with a wildcard, an attempt at explicitly downcasting the new Animal into a Dog is forbidden by the compiler:
Without the "T" type parameter, I can't cast the mismatched object to the type the compiler accepts for the ArrayList.
So here are my questions:
1. Are Sierra and Bates correct when they say
public <T extends Animal> void takeThing(ArrayList<T> list) is the same as
public void takeThing(ArrayList<? extends Animal> list), other than that there is no way to reference the non-existent type parameter?
2. Is the wildcard alternative the better choice when I want to avoid the risk of a type mis-match in a collection? (Alternatively, how does one know when to use which form, wildcard or named type parameter?)