7

Consider below code and the output in Eclipse 4.5.0 & javac(1.8) .

I know it is due to the type erasure in runtime, but why the second one still output data even it is declared as List of Integer, I also checked with javap, checkcast bytecode is only inserted in the third output .

My questions are :

  1. is it a bug ?

  2. How the javac determine where to insert the "cast" ?

    public static void main(String[] args){
    List<String> a = Arrays.asList("abc","def");
    List<Integer> b = (List<Integer>)(List<?>)a;
    System.out.println(b.size()); --output 2
    System.out.println(b.get(1)); ---output "def"
    System.out.println(b.get(1).getClass()); --error in type cast
    

EDIT

Checked the answer below and the When is generic return value of function casted after type erasure? , quite similar with my case. If we add the answer from this What is meant by "the erasure of the static type of the expression on which it is called" in the getClass() docs?, then it will be much clear about the "cast" rule.

The compiler can determine where to insert the cast and ensure type safety.

My first case is fine since it will return int anyway.

Second case is fine since the println expect Object. so no cast is needed to ensure type safety.

Thrid case is not since the getClass() is expected to return the Class which is the static type of b.get(1) according to JLS. So the cast is inserted and get type cast error.

As @newacct said , "you should not rely on the compiler to decide either way" (when there is alternative choice and also ensure type safety ,the 2nd case here).

Community
  • 1
  • 1
Keith
  • 669
  • 6
  • 16
  • #2 works because of type erasure. It no longer cares that you "lied" about it being an Integer as it's forgotten about it being anything other than an Object. I don't know why #3 fails. – Novaterata Apr 15 '17 at 14:30
  • @Novaterata, i do not think so . erasure is not simply remove the parameter variable , but also insert "necessary cast ". Thus the third one has a cast inserted , so it will be ((Integer)b.get(1)).getClass() which will fail in type checking at runtime. – Keith Apr 15 '17 at 14:34
  • #2 and #3 were referring to the print statements – Novaterata Apr 15 '17 at 14:35
  • a and b is definitely a List of plain Object at runtime – Novaterata Apr 15 '17 at 14:36
  • The key word is "necessary" cast. Those print methods are for Object so no cast is necessary – Novaterata Apr 15 '17 at 14:38
  • Aha, I see your point. You mean since the argument for println is Object, so no cast is necessary for the 2nd one, right ? I thought as that in the first place as well . But what i think is : java should add cast in all places except those one which could be left out and also keep type safety. So i could understand 3rd one is wrong , but do not understand why 2nd works – Keith Apr 15 '17 at 14:41
  • . toString() is a method in Object. All objects have hashCode toString equals etc. Therefore they can all be printed – Novaterata Apr 15 '17 at 19:26
  • But they also have getClass. I honestly don't know why #3 fails – Novaterata Apr 15 '17 at 19:30

1 Answers1

0

In the last example, the cast happens as follows

Class clazz = ((Integer)b.get(1)).getClass();

And hence the exception, where the second line

System.out.println(b.get(1));

Does not assign to an integer and hence the cast wont happen, assign it to an integer to see it failing.

Integer x = b.get(1); //fails
Sleiman Jneidi
  • 22,907
  • 14
  • 56
  • 77