Какое изменение JLS 6/7 приводит к тому, что следующий непроверенный код с коллекциями и дженериками работает в Java 7?

Следующий код

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);
}

это правильная Java (хотя код подозрительный). Используя javac и java 6, он бросает

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

в то время как он работает без ошибок при использовании javac и java 7.

Это изменение языка, исправленная ошибка или скрытая функция?

(Примечание: код, скомпилированный с помощью Eclipse, работает без ошибок на всех проверенных версиях Eclipse — от Helios до Kepler.)


person Piotr Findeisen    schedule 28.08.2013    source источник
comment
В Java 6 ? является ? extends Object не сериализуемым. Я предполагаю, что Java 7 более либеральна в отношении того, к чему вы можете привести ?. Если вы просто разыграете (List), это будет работать на обеих версиях.   -  person Peter Lawrey    schedule 28.08.2013
comment
stackoverflow.com/questions/15389994/lazy -class-cast-in-java/   -  person ZhongYu    schedule 28.08.2013
comment
Можете ли вы прокомментировать строку, которая вызывает исключение в Java 6, для ясности? Я предполагаю, что это for (Object el : list) {.   -  person Paul Bellora    schedule 28.08.2013
comment
@Peter, ну... приведенное выше сделано скомпилировано с использованием javac 6, так что... В любом случае, я думаю, имеет значение только фактический тип переменной list, а не то, как я выполнил назначение?   -  person Piotr Findeisen    schedule 29.08.2013
comment
@Paul, отметил, спасибо!   -  person Piotr Findeisen    schedule 29.08.2013
comment
@zhong.j.yu, спасибо за подсказку! Печально, что они не указали это точно :(   -  person Piotr Findeisen    schedule 29.08.2013


Ответы (1)


Вы загрязняете кучу, добавляя свой необработанный Object в коллекцию (для чего вам нужно исполнить танец актеров). Технически это не незаконно, но это ошибка.

Когда вы извлекаете значение из своего неявного итератора, кажется, что компилятор Java 6 выполняет кастинг немедленно, а компилятор Java 7 — нет. Более эффективно не приводить к Serializable, если в этом нет необходимости (поскольку удерживающая переменная просто Object), но это поведение не определено, насколько я понимаю из JLS. Попробуйте запустить javap на ваших двух файлах .class и посмотрите на код вокруг этого цикла for (вероятно, сразу после вызова invokeinterface Iterator.next()).

person chrylis -cautiouslyoptimistic-    schedule 28.08.2013
comment
Спасибо за объяснение. Я знаю, что мой вопрос звучит глупо, но я действительно очень понимаю, что происходит, и знаю, что нужно получать и читать байт-код, и я даже знаю, что любой такой код, как в моем примере, должен быть немедленно исправлен — все это не по теме. Надеюсь, ваше объяснение поможет другим. Мой вопрос заключался в следующем: почему изменилось поведение между Sun/Oracle Java 6 и 7? - person Piotr Findeisen; 29.08.2013
comment
Это был побочный комментарий в моем ответе. ;-) По-видимому, в спецификации не указана точка, в которой требуется неявное приведение, и в этом случае компилятор Java 7 мог заметить с помощью новой логики статического анализа, что вы всегда обрабатывали ссылку, извлеченную из коллекции, как обычный Object в любом случае, и поэтому он не тратит впустую инструкции, выполняющие приведение, которое не имеет значения. - person chrylis -cautiouslyoptimistic-; 29.08.2013