Различия в автоматической распаковке между 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 компилятор (без опции any-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 106

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) от ссылочных типов к примитивам. Это явно перечисляет приведения от объекта к примитивам как сужающее ссылочное преобразование с распаковкой.

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

Итог: Если спецификация. allows (Object) (int) это также должно быть allowing(int) (объект).

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

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;