Различия в автоматической распаковке между Java 6 и Java 7

Я заметил разницу в поведении автоматической распаковки между Java SE 6 и Java SE 7. Мне интересно, почему это так, потому что я не могу найти документацию об изменениях в этом поведении между этими двумя версиями.

Вот простой пример:

Object[] objs = new Object[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

Это прекрасно компилируется с javac из Java SE 7. Однако, если я дам компилятору аргумент "-source 1.6", я получу ошибку в последней строке:

inconvertible types
found   : java.lang.Object
required: int

Я попытался загрузить Java SE 6 для компиляции с собственным компилятором версии 6 (без какой-либо опции -source). Согласен и выдает ту же ошибку, что и выше.

Так что же дает? После еще нескольких экспериментов кажется, что распаковка в Java 6 может распаковывать только те значения, которые явно (во время компиляции) имеют коробочный тип. Например, это работает в обеих версиях:

Integer[] objs = new Integer[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];

Таким образом, кажется, что между Java 6 и 7 функция распаковки была улучшена, так что она могла приводить и распаковывать типы объектов одним махом, не зная (во время компиляции), что значение имеет правильный упакованный тип. Однако, читая Спецификацию языка Java или публикации в блоге, которые были написаны во время выхода Java 7, я не вижу никаких изменений в этой вещи, поэтому мне интересно, что это за изменение и как называется эта "особенность"?

Просто любопытство: из-за изменений можно вызвать "неправильные" распаковки:

Object[] objs = new Float[2];
objs[0] = new Float(5);
int myInt = (int)objs[0];

Это хорошо компилируется, но дает ClassCastException во время выполнения.

Любая ссылка на это?

2 ответа

Решение

Похоже, что язык в разделе 5.5 Преобразование приведения в Java 7 JLS был обновлен по сравнению с тем же разделом в Java 5/6 JLS, вероятно, для пояснения разрешенных преобразований.

Java 7 JLS говорит

Выражение ссылочного типа может подвергаться преобразованию преобразования в примитивный тип без ошибки, распаковывая преобразование.

Java 5/6:

Значение ссылочного типа может быть преобразовано в примитивный тип путем распаковки преобразования (§5.1.8).

Java 7 JLS также содержит таблицу (таблица 5.1) разрешенных преобразований (эта таблица не включена в Java 5/6 JLS) из ссылочных типов в примитивы. Это явно перечисляет приведения от Object к примитивам как сужающее преобразование ссылок с распаковкой.

Причина объясняется в этом письме:

Итог: если спец. позволяет (Object)(int), он также должен разрешать (int)(Object).

Вы правы; Проще говоря:

Object o = new Integer(1234);
int x = (int) o;

Это работает в Java 7, но дает ошибку компиляции в Java 6 и ниже. Как ни странно, эта функция не документирована; например, это не упоминается здесь. Это спорный вопрос, если это новая функция или ошибка исправления (или новая ошибка?), Увидеть некоторые Связанная информация и обсуждение. Похоже, что консенсус указывает на неоднозначность в исходной спецификации, что привело к немного неправильной / несовместимой реализации в Java 5/6, которая была исправлена ​​в 7, потому что это было критично для реализации JSR 292 (динамически типизированных языков).

Java Autoboxing теперь имеет еще несколько ловушек и сюрпризов. Например

Object obj = new Integer(1234);
long x = (long)obj;

скомпилируется, но не получится (с ClassCastException) во время выполнения. Это, вместо этого, будет работать:

long x = (long)(int)obj;

Другие вопросы по тегам