3

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.)

Piotr Findeisen
  • 19,480
  • 2
  • 52
  • 82
  • In Java 6, `?` is `? extends Object` not Serializable I assume Java 7 is more liberal as to what you can cast `?` to. If you just cast `(List)` this will work on both versin. – Peter Lawrey Aug 28 '13 at 11:12
  • 2
    http://stackoverflow.com/questions/15389994/lazy-class-cast-in-java/15391048#15391048 – ZhongYu Aug 28 '13 at 15:05
  • Can you comment the line that throws the exception in Java 6, for clarity? I'm assuming it's `for (Object el : list) {`. – Paul Bellora Aug 28 '13 at 19:47
  • @Peter, well... the above *did* compile using javac 6, so... Anyway, I guess only the actual type of the `list` variable matters, not how I did the assignment? – Piotr Findeisen Aug 29 '13 at 05:31
  • @zhong.j.yu, thanks for the pointer! Sad thing they did not spec this out precisely :( – Piotr Findeisen Aug 29 '13 at 05:42

1 Answers1

2

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()).

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • thank you for the explanation. I know my question sounds silly, but I really really do understand what is going and know what to get and read bytecode, and I know even that any such code as in my example should be fixed immediately -- all this is off-topic. Hopefully your explanation will help others. My question was -- **why** did the behavior change between Sun/Oracle Java 6 and 7? – Piotr Findeisen Aug 29 '13 at 05:34
  • That was an aside comment in my answer. ;-) Apparently the spec doesn't specify the point at which the implicit cast is required, and in this case the Java 7 compiler could observe via new static analysis logic that you were always treating the reference pulled out of the collection as a plain `Object` anyway, and so it didn't waste instructions performing a cast that was irrelevant. – chrylis -cautiouslyoptimistic- Aug 29 '13 at 11:00