Does anyone else find it totally counterintuitive that List<Number> and List<Integer> are unrelated types? That is, they are of course related in that their erasures are both List. But if you have a List<Integer> and want to pass it to a method that expects a List<Number>, well, you can't.
The way to deal with this is to declare that method to be generic itself. Instead of:
Now, I don't know about you, but the second thing is what I want 99.99% of the time. Does it really make sense to have all this extra syntactic nonsense for the common case? Why not have this be the meaning of the first syntactic form, and in the rare case you want exactly List<Number>, and not List<Integer>, use some extra syntax there, like List<final Number> (just to pick an existing keyword)?
OK, let me qualify the above post a little. Whether you want covariance, contravariance, or invariance of your type parameters is something that does vary depending on the situation. For example, if my method wants to append Numbers to the list, I clearly don't want to allow a List<Integer> as the parameter (because I might append a Double, say), but it would be ok to allow a List<Object> -- this is contravariance. And if I want to both read from the list and add to it, well then List<Integer> is not acceptable, and neither is List<Object> -- this is invariance.
I guess my point is that covariance is the intuitively obvious interpretation, because of the way subclasses are always taken as acceptable substitutes for classes in Java. I think that a cumbersome syntax for covariant method arguments is going to be a big gotcha when generics are added to the language -- there will be a lot of APIs written that should accept covariant arguments that won't.
Incidentally, is there a way to express contravariant bounds on a type parameter? I don't see it in the spec.
When you're adding the the list and not reading from it, you want the list's polymorphism to be contravariant. So you can write to a List<Object> as if it was a List<Number>.
When you're reading from the list and not adding to it, you want it to be covariant. So you can read from a List<Integer> as if it was a List<Number>.
But when you're doing both, the only safe option is for it to be invariant. That way, you can't screw up the type-safety either way - either by reading Objects out of what you think is a List<Number>, or by writing Numbers into what's really a List<Integer>.
When you pass a reference in Java, it doesn't know if you mean to read from it, write to it, go to bed with it or whatever. So the designers had no choice but to make it invariant.
Have a look at the RFE for const references and methods for the only (heavily convoluted) way around this:
http://developer.java.sun.com/developer/bugParade/bugs/4211070.html
When you pass a reference in Java, it doesn't know if
you mean to read from it, write to it, go to bed with
it or whatever. So the designers had no choice but to
make it invariant.
I disagree. The whole point of this addition to the language is to make it more expressive of your intent. The author of the method can expressly say whether he intends to read from, write to, or both, the argument, by declaring the type covariant, contravariant, or invariant. The default they have chosen is invariant, which I believe will cause more confusion than covariant would have.
so you want to add extra keywords for covariant and contravariant (remember, you can't change the meaning of the existing case) so that people can say: