Почему Java + +, - =, * =, / = составные операторы присваивания не требуют приведения?

3160

До сегодняшнего дня я думал, что, например:

i += j;

Был просто ярлык для:

i = i + j;

Но если мы попробуем это:

int i = 5;
long j = 8;

Тогда i = я + j; не будет компилироваться, но i += j; будет компилировать штраф.

Означает ли это, что на самом деле i += j; является ярлыком для чего-то вроде этого i = (type of i) (i + j)?

  • 123
    Я удивлен, что Java допускает это, будучи более строгим языком, чем его предшественники. Ошибки в приведении могут привести к критическому отказу, как в случае с Ariane5 Flight 501, где 64-разрядное преобразование с плавающей точкой в 16-разрядное целое число привело к сбою.
  • 83
    В системе управления полетом, написанной на Java, это меньше всего беспокоит вас @SQLDiver
Показать ещё 9 комментариев
Теги:
operators
casting
variable-assignment
assignment-operator

11 ответов

2241
Лучший ответ

Как всегда с этими вопросами, 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);

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

  • 39
    Поэтому i+=j компилирую, как я проверял сам, но это приведет к потере точности, верно? Если это так, то почему это не происходит и в i = i + j? Зачем нас там пугать?
  • 41
    @ronnieaka: Я предполагаю, что разработчики языка чувствовали, что в одном случае ( i += j ) безопаснее предположить, что потеря точности желательна в отличие от другого случая ( i = i + j )
Показать ещё 10 комментариев
472

Хорошим примером этой отливки является использование * = или /=

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'
  • 5
    @SajalDutta Я не получил эту ссылку. Разум объясняешь?
  • 9
    @AkshatAgarwal ch это символ. 65 * 1,5 = 97,5 -> Понял?
Показать ещё 3 комментария
242

Очень хороший вопрос. Спецификация Java Language подтверждает ваше предложение.

Например, следующий код верен:

short x = 3;
x += 4.6;

и приводит к тому, что x имеет значение 7, потому что оно эквивалентно:

short x = 3;
x = (short)(x + 4.6);
  • 6
    Или веселее: "int x = 33333333; x + = 1.0f;".
  • 3
    @supercat, что это за обман? Расширяющееся преобразование, которое неправильно округлено, сопровождаемое дополнением, которое фактически не изменяет результат, снова приводит к int, чтобы получить результат, который является наиболее неожиданным для нормального человеческого разума.
Показать ещё 2 комментария
161

Да,

в основном, когда мы пишем

i += l; 

компилятор преобразует это значение в

i = (int)(i + l);

Я только что проверил код файла .class.

Действительно хорошая вещь, чтобы знать

  • 2
    Можете ли вы сказать мне, что это за файл?
  • 6
    @hexafraction: что вы подразумеваете под файлом класса? если вы спрашиваете о файле класса, который я упоминал в своем посте, то это версия вашего Java-класса
Показать ещё 5 комментариев
90

вам нужно сделать от 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.

но в случае += он просто отлично работает, потому что оператор неявно выполняет литье типов из типа правой переменной в тип левой переменной, поэтому не нужно явно указывать.

  • 7
    В этом случае «неявное приведение» может привести к потерям. В действительности, как @LukasEder утверждает в своем ответе, приведение к int выполняется после + . Компилятор (должен?) Выдать предупреждение, если он действительно приведёт long к int .
59

Проблема здесь связана с литьем типов.

Когда вы добавляете int и long,

  • Объект int заносится в long и оба добавляются, и вы получаете длинный объект.
  • но длинный объект не может быть неявным образом передан в int. Итак, вы должны сделать это явно.

Но += закодирован таким образом, что он набирает тип. i=(int)(i+m)

50

В преобразованиях типа Java выполняется автоматически, когда тип выражения в правой части операции присваивания можно безопасно продвигать к типу переменной в левой части задания. Таким образом, мы можем смело назначить:

 byte -> short -> int -> long -> float -> double. 

То же самое не будет работать наоборот. Например, мы не можем автоматически преобразовать long в int, потому что первое требует большего объема памяти, чем второе, и, следовательно, информация может быть потеряна. Чтобы заставить такое преобразование, мы должны выполнить явное преобразование.
Тип - Конверсия

  • 2
    Эй, но long в 2 раза больше, чем float .
  • 9
    Число с float не может содержать все возможные значения типа int , а double не может содержать все возможные значения типа long .
Показать ещё 3 комментария
39

Иногда такой вопрос можно задать на собеседовании.

Например, когда вы пишете:

int a = 2;
long b = 3;
a = a + b;

нет автоматического приведения типов. В С++ не будет никакой ошибки компиляции вышеуказанного кода, но в Java вы получите что-то вроде Incompatible type exception.

Чтобы избежать этого, вы должны написать свой код следующим образом:

int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
  • 6
    Спасибо за понимание относительно сравнения использования op в C ++ с его использованием в Java. Мне всегда нравится видеть эти мелочи, и я думаю, что они вносят свой вклад в разговор, который часто может быть пропущен.
  • 0
    Однако сам вопрос интересен, с просьбой об этом в интервью глупо. Это не доказывает, что человек может создать код хорошего качества - это просто доказывает, что у него было достаточно терпения, чтобы подготовиться к экзамену на получение сертификата Oracle. И «избегание» несовместимых типов с помощью опасного автоконвертации и, таким образом, сокрытие возможной ошибки переполнения, вероятно, даже доказывает, что человек не способен произвести, вероятно, код хорошего качества. Будь прокляты авторы Java для всех этих автоконверсий и авто-бокса и все!
19

Основное отличие состоит в том, что с 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);
  • 5
    Составные операторы присваивания выполняют сужающее преобразование результата бинарной операции, а не правого операнда. Таким образом, в вашем примере «a + = b» не эквивалентно «a = a + (int) b», но, как объясняется другими ответами здесь, «a = (int) (a + b)».
8

Тонкая точка здесь...

Существует неявный i+j для i+j когда j является double, а i - int. Java ALWAYS преобразует целое число в double при выполнении операции между ними.

Чтобы прояснить i+=j где i - целое число, а j - двойное, можно описать как

i = <int>(<double>i + j)

См. Это описание неявного литья

В этом случае вам может потребоваться приведение типа j к (int) для ясности.

  • 1
    Я думаю, что более интересный случай может быть int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat; , Добавление нуля к someInt не должно влиять на его значение, но продвижение someInt для float может изменить его значение.
-4

В случае оператора оператора присваивания выполняется автоматический запуск внутреннего типа:

byte b1 = 10;
//b1 = b1 + 1; Compile time error because found int
System.out.println(b1);
byte b3 = 10;
b3 += 1; //in compound assignment implicit type casting will be performed simultaneously
System.out.println("b3=: "+b3);
byte b4 = 127;
b4 += 3; //in compound assignment implicit type casting will be performed simultaneously
System.out.println("b4=: "+b4);//-126

И в некоторых случаях вы потеряете некоторые значения:

int i = 1;
i += 1.5;
System.out.println("i=: "+i); //will print 2, and you lost .5 !!!
Сообщество Overcoder
Наверх
Меню