Почему 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 в intexplicitly в случае 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,

  1. Объект int приводится к long, и оба добавляются, и вы получаете long объект.
  2. но длинный объект не может быть неявно приведен к 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);

Другими словами, ваше предположение верно.

Другие вопросы по тегам