Почему не сложное назначение в проблемах переполнения ловушки Java?

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

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value += increment;
}

В то время как это дает ошибку времени компиляции, как и следовало ожидать:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value = value + increment;
}

Я проверил это и действительно, JLS (раздел 15.26.2) имеет это, чтобы сказать:

Совокупное присваивание выражения вида E1 op = E2 эквивалентно E1 = (T) ((E1) op (E2)), где T - тип E1, за исключением того, что E1 оценивается только один раз.

Это кажется мне смешным. Почему они чувствовали необходимость явно бросить здесь? Кажется, что автоматическое преобразование типов будет обрабатывать расширение в любом случае, и автоматическое сужение, как это, в значительной степени гарантированно приведет к переполнению целых чисел.

Ответы

Ответ 1

Здесь есть одно объяснение:

Когда вы выполняете присвоение (первый фрагмент кода), java обеспечивает проверку типов, поскольку LHS и RHS могут быть очень независимы друг от друга.

Но сложный оператор больше похож на инкрементный оператор. + = изменяет значение переменной а не назначать новую значение переменной. Когда вы меняете байт, вы ожидаете, что байт будет результат. Чтобы сделать жизнь проще, java делает неявное преобразование типа для составных операторов, потому что они модификаторы.

Ответ 2

Сопряженные операторы присваивания определяются JLS (15.26.2) следующим образом:

"Совокупное выражение выражения формы E1 op= E2 эквивалентно

      E1 = (T)((E1) op (E2))`, 

где T - тип E1, за исключением того, что E1 оценивается только один раз. "

В этом случае E1 имеет тип int E2 имеет тип long, а op - +. Таким образом, это эквивалентно:

value = (int)(value + increment);

Добавление int и a long дает long, а затем возвращается к int перед назначением. Все в порядке, следовательно, ошибка компиляции.

Разница между этим и простым присваиванием (т.е. value = value + increment;) заключается в том, что простое присваивание не имеет типа.


ОК, так почему они определили его таким образом?

Я думаю, что причина состоит в том, чтобы привести примеры, подобные этой работе:

    byte b = ...
    b += 1;

Без typecast b += 1 будет ошибкой компиляции, и вам нужно будет написать ее как:

    b += (byte) 1;

Ответ 3

Эта ссылка проанализировала проблему, которую вы затронули.

Изменчивое поведение для возможной потери точности

Чтобы избежать неприятных сюрпризов, не делайте использовать составные операторы присваивания переменные байта типа, короткие или char. При использовании составного назначения операторов по переменным типа int, убедитесь, что выражение на правая часть не имеет длинного типа, поплавок или двойной. При использовании соединения операторов присваивания по переменным type float, убедитесь, что выражение в правой части не относится к типу двойной. Этих правил достаточно для предотвращать создание компилятора опасные сужающиеся приведения.