Почему в Java не возникает проблем с переполнением? - PullRequest
13 голосов
/ 30 марта 2011

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

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 оценивается только один раз.

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

Ответы [ 3 ]

4 голосов
/ 30 марта 2011

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

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

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

2 голосов
/ 30 марта 2011

Составные операторы присваивания определяются 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 и long дает long, который затем возвращается к int перед назначением.Все в порядке, следовательно, нет ошибки компиляции.

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


Ладно, так почему они определили это таким образом?

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

    byte b = ...
    b += 1;

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

    b += (byte) 1;
1 голос
/ 30 марта 2011

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

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

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

...