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:
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?)
"Il y a peu de choses qui me soient impossibles..."
Stevens Miller wrote:Is the wildcard alternative the better choice...
In my experience, if it really is an "alternative", the answer is probably 'no'.
FYI, my rules of thumb:
1. Generics is an addition to the language, and actually works a bit like a pre-processor. For that reason, it's not a 100% solution, and for that reason, it's usually best to keep it "as simple as possible, but no simpler".
2. Avoid wildcards if you possibly can. Exception: T extends Comparable<? super T> (and similar constructs).
Your example is also quite a nightmare mixture of generics, static, and potential lambdas - the latter of which I'm not (yet) qualified to give advice on - so I suspect you're asking for trouble.
Point 1 above notwithstanding, I've generally found that when I run into problems with generics, it's almost always because I'm overthinking things (often because I'm trying to use generics for the sort of stuff that is really a runtime check); and there's often a much simpler solution available.
So perhaps you should get back to basics and ask yourself: What is the intent of all this?
"Leadership is nature's way of removing morons from the productive flow" - Dogbert
Articles by Winston can be found here