Почему Java + +, -=, *=, /= составные операторы присваивания не требуют приведения?
До сегодняшнего дня я думал что например
i += j;
Был просто ярлык для:
i = i + j;
Но если мы попробуем это:
int i = 5;
long j = 8;
затем i = i + j;
не скомпилирует но i += j;
скомпилирует нормально.
Означает ли это, что на самом деле i += j;
это ярлык для чего-то вроде этогоi = (type of i) (i + j)
?
12 ответов
Как всегда с этими вопросами, JLS держит ответ. В этом случае §15.26.2 Операторы сложного присваивания. Выписка:
Составное выражение присваивания формы
E1 op= E2
эквивалентноE1 = (T)((E1) op (E2))
, гдеT
это типE1
, Кроме этогоE1
оценивается только один раз.
Пример, приведенный в §15.26.2
[...] следующий код правильный:
short x = 3; x += 4.6;
и в результате x имеет значение 7, потому что оно эквивалентно:
short x = 3; x = (short)(x + 4.6);
Другими словами, ваше предположение верно.
Хорошим примером этого кастинга является использование *= или /=
byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57
или же
byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40
или же
char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'
или же
char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'
Очень хороший вопрос Спецификация языка Java подтверждает ваше предложение.
Например, следующий код является правильным:
short x = 3; x += 4.6;
и в результате x имеет значение 7, потому что оно эквивалентно:
short x = 3; x = (short)(x + 4.6);
Да,
в основном, когда мы пишем
i += l;
компилятор преобразует это в
i = (int)(i + l);
Я только что проверил .class
код файла.
Действительно хорошая вещь, чтобы знать
Вам нужно изгнать из long
в int
explicitly
в случае i = i + l
тогда он скомпилируется и даст правильный вывод. лайк
i = i + (int)l;
или же
i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.
но в случае +=
это просто работает нормально, потому что оператор неявно выполняет приведение типа из типа правой переменной в тип левой переменной, поэтому нет необходимости явного преобразования.
Проблема здесь заключается в приведении типов.
Когда вы добавляете int и long,
- Объект int приводится к long, и оба добавляются, и вы получаете long объект.
- но длинный объект не может быть неявно приведен к int. Итак, вы должны сделать это явно.
Но +=
закодирован таким образом, что он выполняет приведение типов. i=(int)(i+m)
В Java преобразования типов выполняются автоматически, когда тип выражения в правой части операции присваивания можно безопасно преобразовать в тип переменной в левой части присваивания. Таким образом мы можем смело назначать:
byte -> short -> int -> long -> float -> double.
То же самое не будет работать наоборот. Например, мы не можем автоматически преобразовать long в int, потому что первое требует больше памяти, чем второе, и, следовательно, информация может быть потеряна. Чтобы вызвать такое преобразование, мы должны выполнить явное преобразование.
Тип - Конверсия
Иногда такой вопрос можно задать на собеседовании.
Например, когда вы пишете:
int a = 2;
long b = 3;
a = a + b;
нет автоматической типизации. В C++ не будет ошибок при компиляции приведенного выше кода, но в Java вы получите что-то вроде Incompatible type exception
,
Поэтому, чтобы избежать этого, вы должны написать свой код следующим образом:
int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
Основное отличие состоит в том, что с a = a + b
, здесь не происходит типизирование, поэтому компилятор злится на тебя за отсутствие типизации. Но с a += b
то, что он действительно делает, это приведение типов b
к типу, совместимому с a
, Так что если вы делаете
int a=5;
long b=10;
a+=b;
System.out.println(a);
Что вы на самом деле делаете:
int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);
Тонкий момент здесь...
Существует неявный тип для i+j
когда j
двойной и i
является инт.
Java ВСЕГДА преобразует целое число в двойное, когда между ними есть операция.
Уточнить i+=j
где i
является целым числом и j
двойной может быть описан как
i = <int>(<double>i + j)
Смотрите: это описание неявного приведения
Вы могли бы хотеть Typecast j
в (int)
в этом случае для ясности.
Спецификация языка Java определяет E1 op= E2
быть эквивалентным E1 = (T) ((E1) op (E2))
где T
это тип E1
а также E1
оценивается один раз.
Это технический ответ, но вам может быть интересно, почему это так. Что ж, давайте рассмотрим следующую программу.
public class PlusEquals {
public static void main(String[] args) {
byte a = 1;
byte b = 2;
a = a + b;
System.out.println(a);
}
}
Что печатает эта программа?
Вы догадались 3? Жаль, что эта программа не скомпилируется. Зачем? Ну, так сложилось, что добавление байтов в Java определено для возврата int
, Я полагаю, это произошло потому, что виртуальная машина Java не определяет байтовые операции для сохранения в байтовых кодах (в конце концов, их число ограничено), а использование целочисленных операций - это деталь реализации, представленная на языке.
Но если a = a + b
не работает, это будет означать a += b
никогда не будет работать для байтов, если это E1 += E2
был определен как E1 = E1 + E2
, Как показывает предыдущий пример, это действительно так. Как взломать, чтобы сделать +=
оператор работает для байтов и шортов, подразумевается неявное приведение. Это не так здорово, но во время работы над Java 1.0 основное внимание уделялось выпуску языка с самого начала. Теперь, из-за обратной совместимости, этот хак, введенный в Java 1.0, не может быть удален.
Как всегда на эти вопросы, JLS знает ответ. В этом случае §15.26.2 Составные операторы присваивания. Выдержка:
Составное выражение присваивания формы E1 op= E2 эквивалентно E1 = (T)((E1) op (E2)), где T — тип E1, за исключением того, что E1 вычисляется только один раз.
Пример приведен из §15.26.2.
[...] следующий код верен:
short x = 3;
x += 4.6;
и приводит к тому, что x имеет значение 7, поскольку это эквивалентно:
short x = 3;
x = (short)(x + 4.6);
Другими словами, ваше предположение верно.