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