Hi,
to understand the difference between the two programs, you could use
a decompiler (jad, for example), to see what is really in the bytecode.
By doing this, you can see that there is a reason why these two programs
behave differently; in fact, there are different !
For the first code,
System.out.println(intList.get(1));
becomes in the bytecode :
System.out.println((String)intList.get(1));
hence, the ClassCastException (Integer cannot be cast in String).
But, for the second code,
System.out.println(intList.get(1));
becomes in the bytecode :
System.out.println(intList.get(1));
there is no cast added, and therefore, no runtime exception !
To understand why the two codes are not compiled the same way, we have to
understand how the
java compiler handles type safety with generics. When a
method has a generics return type, the compiler adds an extra cast if
needed, that is to say, if the result is assigned in a variable of the
same generic type. But if the result is assigned in a Object variable, no
cast is added. You can see it in this example :
List<String> listString = new ArrayList<String>();
Object o = listString.get(1);
String s = listString.get(1);
becomes in the bytecode :
List listString = new ArrayList();
Object o = listString.get(1);
String s = (String)listString.get(1);
(remember that generics are removed in the bytecode, this is called type erasure).
We have made half the way to reach the solution.
The other key point to understand in this case is that System.out.println
has two overloaded versions, one taking an Object parameter, and one taking
a String parameter. Remember that the compiler choose the method to call at
compile time using the most specific type.
We have now all the tools to solve our case.
For the program one :
the compiler knows that intList.get(1) is a String object. It searches
among the overloaded versions of println which one it should call. There
is a version taking a String, so it selects this one. It means assigning
into a String variable, so a (String) cast is added.
For the program two :
the compiler knows that intList.get(1) is a Integer object. It searches
among the overloaded version of println which one it should call. The most
specific is the one taking an Object parameter, it takes this one. It means
assigning into an Object variable, so no cast is added.
The two programs have now two different bytecodes leading to two very
different behaviors.
Gilles
[ March 08, 2008: Message edited by: Gilles Marceau ]
[ March 08, 2008: Message edited by: Gilles Marceau ]