public void wahey(List<Object> list) {}
wahey(new LinkedList<Number>());
The call to the method will not type-check. I can't even cast the parameter as follows:
wahey((List<Object>) new LinkedList<Number>());
From my research, I have gathered that the reason for not allowing this is type-safety. If we were allowed to do the above, then we could have the following:
List<Double> ld;
wahey(ld);
Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object>
reference). Now, after the method call, ld refers to a list with a type List<Double>
, but the actual list contains some String objects!
This seems different to the normal way Java works without generics. For instance:
Object o;
Double d;
String s;
o = s;
d = (Double) o;
What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile.
This leads me to believe this is purely a design decision with regards to the type restrictions on generics. I was hoping to get some comments on this decision?
What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. The equivalent with generics would be:
Which behaves the same way (compiles, but fails at runtime). The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. For example:
This looks perfectly fine and type-safe until you call it like this:
Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters.
So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. That's bad, because it's hidden and therefore hard to avoid and harder to debug.
What you're observing makes perfect sense when you consider that the main purpose of Java generics is to get type incompatibilities to fail at compile time instead of run time.
From java.sun.com
In Java,
List<S>
is not a subtype ofList<T>
whenS
is a subtype ofT
. This rule provides type safety.Let's say we allow a
List<String>
to be a subtype ofList<Object>
. Consider the following example:So, forcing this provides type safety but it also has a drawback, it's less flexible. Consider the following example:
Here you have a collection of
T
's, you want to add all items from another collection. You can't call this method with aCollection<S>
for anS
subtype ofT
. Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection.To fix this, Java provides what they call "wildcards". Wildcards are a way of providing covariance/contravariance. Now consider the following using wildcards:
Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). This way we get flexibility and type safety.
There are essentially two dimensions of abstraction here, the list abstraction and the abstraction of its contents. It's perfectly fine to vary along the list abstraction - to say, for instance, that it's a LinkedList or an ArrayList - but it's not fine to further restrict the contents, to say: This (list which holds objects) is a (linked list which holds only numbers). Because any reference that knows it as a (list which holds objects) understands, by the contract of its type, that it can hold any object.
This is quite different from what you have done in the non-generics example code, where you've said: treat this String as if it were a Double. You are instead trying to say: treat this (list which holds only numbers) as a (list which holds anything). And it doesn't, and the compiler can detect it, so it doesn't let you get away with it.
Consider if it was...
You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. Thus, it is not allowed.
They aren't subtypes of each other due how generics work. What you want is to declare your function like this:
Then it will accept a List of anything that extends Object. You can also do:
This will let you take in Lists of something that's a subclass of Number.
I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler.