Search code examples
javagenericsjavacjls

What change of JLS 6/7 causes the following unchecked code with collections and generics to work in Java 7?


The following code

import java.util.*;
import java.io.*;

@SuppressWarnings("unchecked")
List<Serializable> list = (List<Serializable>) (List<?>)
  Collections.singletonList(new Object());

for (Object el : list) { // -> ClassCastException
  System.out.println(el);
}

is correct Java (even though the code is suspicious). Using javac and java 6 it throws

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.io.Serializable

while it runs without error when using javac and java 7.

Is it a language change, fixed bug or a hidden feature?

(Note: Code compiled with Eclipse runs without error on all Eclipse versions checked - Helios to Kepler.)


Solution

  • You're polluting the heap by adding your raw Object to the collection (which you're having to do the cast dance to make happen). It's not technically illegal, but it is a bug.

    When you are pulling the value out of your implicit iterator, the Java 6 compiler appears to be casting immediately, while the Java 7 compiler isn't. It's more efficient not to cast to Serializable if it doesn't need to (since the holding variable is just Object), but this behavior is undefined as far as I understand from the JLS. Try running javap on your two .class files and looking at the code right around that for loop (probably right after an invokeinterface call to Iterator.next()).