Любая идея, почему мне нужно привести здесь целочисленный литерал к (int)?

122

В следующем примере

int i = -128;
Integer i2 = (Integer) i; // compiles

Integer i3 = (Integer) -128; /*** Doesn't compile ***/

Integer i4 = (Integer) (int) -128; // compiles
Integer i4 = -128; // compiles
Integer i5 = (int) -128; // compiles
Integer i6 = (Integer) (-128); // compiles
Integer i7 = (Integer) 0-128; // compiles

Я не могу бросить -128с , (Integer)но я могу бросить (int) -128.

Я всегда думал, что -128это intтип, и приведение его (int)должно быть излишним.

Ошибка на линии с i3IS

cannot find symbol variable Integer

Я пробовал это с обновлением Java 6 29 и обновлением Java 7 1.

РЕДАКТИРОВАТЬ: вы получаете то же поведение +128вместо -128. Похоже, что это путаница между унарными и бинарными операторами.

Питер Лоури
источник
5
какой у тебя компилятор? Integer i = -128;однако это должно компилироваться.
bestsss
странный, Integer i3 = (Integer) (-128);хотя и соответствует.
Eng. Fouad
2
@ Eng.Fouad, Peter, унарные символы (+ -) имеют ассоциативность справа налево, а плюс, минус - слева направо. Эффект от -128 будет таким же, как и +128, и если поставить 0 впереди, это должно исправить, то есть 0-128 или 0 + 128. (не могу проверить банкомат, но держу пари, что будет)
bestsss
Хороший вопрос! Я лично хотел бы увидеть ссылку JLS для разрешения унарных / бинарных операторов и когда приведение рассматривается как выражение. В противном случае возможно, что другие компиляторы не сочтут это ошибкой!
Bringer128,
1
Также, к вашему сведению, ошибка, которую я получаю в своей среде IDE, находится Expression expectedтам Integer.
Bringer128,

Ответы:

151

Компилятор пытается вычитать 128из (Integer)вместо преобразования -128в Integer. Добавить, ()чтобы исправить

Integer i3 = (Integer) -128; // doesn't compile
Integer i3 = (Integer) (-128); // compiles

Согласно BoltClock в комментариях, приведение к работе intработает по назначению, потому что это зарезервированное слово и поэтому не может быть интерпретировано как идентификатор, что имеет смысл для меня.

И Bringer128 нашел JLS Reference 15.16 .

 CastExpression:
    (PrimitiveType Dims opt ) UnaryExpression
    (Тип ссылки) UnaryExpressionNotPlusMinus

Как видите, приведение к примитивному типу требует любого UnaryExpression, тогда как приведение к ссылочному типу требует UnaryExpressionNotPlusMinus. Они определены непосредственно перед CastExpression в JLS 15.15 .

Йенс Шаудер
источник
31
Я думаю, это потому, что intэто ключевое слово в Java, но Integerэто не так. Поскольку intэто ключевое слово, вы не можете использовать его в качестве идентификатора переменной или класса, оставляя единственную возможность для него быть приведением типа. Это объяснило бы это.
BoltClock
@BoltClock Включил ваш комментарий в ответ.
Йенс Шаудер,
3
Чтобы сделать это еще более звездным ответом, вы хотите добавить мою ссылку в JLS?
Bringer128,
3
Интересная (для меня) загвоздка в этом вопросе заключается в том, как мы решаем аналогичную проблему в C #, которая также имеет двусмысленность в грамматике между «заключенным в скобки выражением как операнд для двоичного оператора вычитания» и «оператором приведения, где правый операнд cast - это унарное минус-выражение ". См. Раздел 7.7.6 спецификации C # для подробного описания эвристики, которую мы используем, чтобы попытаться решить проблему несоответствия.
Эрик Липперт
1
@BillK Почему ты так говоришь? Спецификация C # не относится к перегрузке операторов в разделе 7.7.6, поэтому для них это не было проблемой.
Bringer128,
48

Я нашел ссылку на JLS. 15.16 .

 CastExpression:
    (PrimitiveType Dims opt ) UnaryExpression
    (Тип ссылки) UnaryExpressionNotPlusMinus

Как видите, приведение к примитивному типу требует любого UnaryExpression, тогда как приведение к ссылочному типу требует UnaryExpressionNotPlusMinus. Они определены непосредственно перед CastExpression в JLS 15.15 .

Вам нужно либо изменить приведение к примитивному типу:

... (int) -128;

Или вы можете изменить выражение справа от приведения на унарное выражение, отличное от плюс-минус:

... (Integer) (-128);  // Either
... (Integer) 0 - 128; // Or
Bringer128
источник
12

Компилятор интерпретирует -оператор как оператор «минус» с двумя аргументами, т.е. он пытается вычесть 128 из некоторого другого указанного числа Integer, но такой переменной в области видимости нет.

Это компилирует:

Integer i3 = (Integer) (-128)
Barend
источник
Вы можете добавить комментарий, почему (int)имеет значение.
Питер Лоури,
1
Это из-за автобокса, разве нет?
Брайан Роуч
9

Возможно, это связано с синтаксическим анализом. Заметь

Integer i4 = (Integer) (-128); 

работает нормально.

В общем, вы не должны приводить к классу Integer. Это включает в себя то, что называется автоматическим блокированием, и может вызвать некоторые незаметные ошибки в вашем коде. Предпочтительный способ делать то, что вы хотите:

Integer i6 = Integer.valueOf(-128)
Кристиан Цибульский
источник
1
cast to Integer - это в точности синтетический сахар для valueOf.
bestsss
4
да, но иногда синтетический сахар плохо себя чувствует. Мне было сложно отследить исключения нулевого указателя в больших приложениях из-за автоматической упаковки. Мы дошли до того, что стали рассматривать автобокс как ошибку, чтобы избавить от головной боли в будущем. Магия - это хорошо, но когда она терпит неудачу, голова болит. Я считаю, что лучше быть откровенным и избавить себя от головной боли.
Кристиан Цибульски
NPE - это плохо с исходящими сообщениями, правда. Случаи Esp, такие как for (int i in Collection<Integer>)b / c, NPE находится в совершенно неожиданном месте. На самом деле я не использую Integer с автобоксингом, поскольку диапазон кеширования невелик (хотя его можно увеличить с помощью параметра XX), но у меня есть класс IntegerProvider (с версии 1.1), чтобы делать то же самое. Использование карты (любого из java.util) Integer-> Anything обычно снижает производительность, если только оно не используется в тривиальных случаях, и почти всегда есть лучшее решение.
bestsss
Приведение int к Integer никогда не может вызвать никаких ошибок, кроме, возможно, переполнения кучи. Однако обратное неверно.
Инго
@MattBall, я не совсем понимаю, синтетический сахар используется широко: eggcorns.lascribe.net/forum/viewtopic.php?id=4400 и синтетические звуки мне лучше.
bestsss
9

Он анализирует его как Integer <minus operator> 128и не находит переменную Integer. Вам нужно будет заключить -128в скобки:

Integer i3 = (Integer) (-128);  // compiles
Богемный
источник
Я поставил +1 ко всем остальным ответам, потому что они тоже все верны :)
Bohemian
7
Integer i3 = (Integer) (-128);

Проблема в том, что -компилятор видит в нем оператора.

Брайан Роуч
источник
6

Строка 3 интерпретируется так, как будто вы пытаетесь вычесть 128 из выражения в круглых скобках, а выражение в скобках не является выражением типа int (оно обрабатывает '-' как оператор '-'). Если вы измените выражение на:

Integer i3 = (Integer) (-128);

тогда компилятор поймет, что «-» - это унарный минус, обозначающий отрицательное целое число.

Уди Коэн
источник
3

Компилятор C # ведет себя так же. Это дает лучший намек, почему он не компилируется:

Чтобы привести отрицательное значение, вы должны заключить значение в круглые скобки

JefClaes
источник