Given below is the code that threw me into confusion; line # 30 in particular. Since both the generic parameters being passed to method 'add' are of type Shape, I assumed that for this call to method 'add', the generic type would be inferred to 'Shape'. But since it doesn't generate a class cast exception at line# 30, it is obvious that generic type is inferred as 'Object' and not Shape.
Could someone please explain the way generic types are inferred when a generic method is called?
Actually, that was sort of surprising to me too, but I guess it makes sense given how Java implements generics. Generics are a late addition to the Java language and could be called a bit of a kludge in the sense that they only serve as hints to the compiler, and don't affect the resulting byte code. That is, although the basic Collection class became Collection<T> when generics were introduced, the byte codes for the two are the same. That's good because it allows older Java applications to run without modification on newer JVMs, but has some drawbacks too.
If you declare a collection to contain type ... Shape, for example ... the compiler will tell you if you try to add a non-Shape to it, and won't make you cast elements you retrieve to Shape. That's good and it saves a lot of tedious coding. However, if you try to cast a random object to type T, like this example, you won't see an error, because the runtime doesn't know what T is. It considers it to be Object, unless you specifically make T extend some other class. That exposes some weakness in Java's implementation of generics, but fortunately, it takes a fairly contrived example to do it.
I don't really see the problem. The compiler is kind enough to warn us that we're using unchecked operations. And because we ignore it we get punched in the face with an ClassCastException when we try to use it.
If you don't know what caused this then read about type erasure.
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." --- Martin Fowler
Please correct my English.
Everyone seems to stick with the fact that type info in generics is lost after compilation and that at runtime its just the generic type specified to the left of the "<" that finally matters. If so, how does the following program work?
Output is: hithere and 3 as expected. How does it delegate the call to the correct method at runtime?
PS:- In helios, this file is not compiling. Is there a workaround for that?
Rob Spoor wrote:What's the error you get? Is it perhaps the varargs on line 16?
The error says:
"Method add(Collection<Integer>) has the same erasure add(Collection<E>) as another method"
"Method add(Collection<String>) has the same erasure add(Collection<E>) as another method"
against the 2 method definitions.
In an earlier version of eclipse, its getting compiled successfully, though I have now forgotten which version that was now.
Helios has it right. Older versions do not give a compile error, while they should...
Not sure what the verison of JDK used by the Helios here (Or it may have added some constraints which are not enforced by the JDK even the JLS say so) ...
I don't get an error on JDK 1.6.0_07 (no IDE), have to check on another version and in the IDE too...
Because of type erasure, the methods get turned into "public int add(Collection)" and "public String add(Collection)". As you see both methods now have the same name and parameter list. Every compiler should fail compilation. If one doesn't then that compiler is actually broken.