Приведение Java: неверен ли компилятор, или спецификация языка неверна, или я ошибаюсь? - PullRequest
20 голосов
/ 22 марта 2011

Я читал спецификацию языка Java, 3-е издание, и обнаружил, как мне кажется, несоответствие между спецификацией и реализацией компилятора javac. Такие же расхождения существуют в компиляторе Eclipse.

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

Ошибка времени компиляции, если тип операнда во время компиляции никогда не может быть приведен к типу, указанному оператором приведения в соответствии с правилами преобразования приведения (§5.5). В противном случае во время выполнения значение операнда преобразуется (при необходимости) путем преобразования преобразования к типу, указанному оператором преобразования.

Раздел 5.5 говорит о конвертации каста. Это дает список типов конверсии, которые разрешены. Конкретно отсутствует в списке «преобразование без коробки с последующим расширением / сужением преобразования примитивов». Однако эта точная последовательность преобразований, по-видимому, разрешена компилятором javac (а также компилятором Eclipse). Например:

long l = (long) Integer.valueOf(45);

... компилируется просто отлично. (Проблематичным является приведение к long; аргумент имеет тип java.lang.Integer, поэтому преобразование требует распаковки в int с последующим расширением примитивного преобразования).

Аналогично, согласно JLS не должно быть возможности приводить от byte до char, потому что это (согласно 5.1.4 ) требует расширяющегося примитивного преобразования и сужающее примитивное преобразование - однако, это приведение также разрешено компиляторами.

Может кто-нибудь просветить меня?

Редактировать: с тех пор, как я спросил об этом, я подал отчет об ошибке в Oracle. Их ответ заключается в том, что это «сбой в JLS».

Ответы [ 2 ]

3 голосов
/ 22 марта 2011

Я думаю, что вы правы, компиляторы верны, а спецификация неверна ....

Это компилирует: (Object)45, а это не так: (Long)45

Единственный способ разобраться в поведении компиляторов (включая использование Intellij, которое я использую) - это если Преобразование Приведения изменено в соответствии с Преобразованием Присвоения и Преобразованием Вызова метода:

  • преобразование бокса (§5.1.7) необязательно, с последующим расширением эталонного преобразования

  • преобразование без распаковки (§5.1.8), за которым необязательно следует расширение с примитивным преобразованием.

плюс

  • расширяющаяся и сужающаяся примитивная конвекция

В спецификации говорилось: «Преобразования с преобразованием являются более инклюзивными, чем преобразования с использованием присваивания или вызова метода: приведение может выполнять любое разрешенное преобразование.кроме преобразования строки или преобразования захвата "

1 голос
/ 22 марта 2011

Насколько я понимаю, приведение от int к long разрешено этим пунктом:

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

Преобразование int в long является расширяющимся примитивным преобразованием .

Это просто оставляет преобразование из Integer в int, что соответствует последнему пулу:

конвертирование распаковки

Конечно, приведение к long в этом примере даже не требуется.

Рассмотрим следующие четыре определения:

final Integer io = Integer.valueOf(45);
final int i = io;
final long l1 = (long)i;
final long l2 = i;

Считаете ли вы кого-нибудь из них удивительным? Ваш оригинальный пример не выглядит иначе; он просто исключает промежуточные переменные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...