Вот объяснение в терминах спецификации языка Java.
Раздел целочисленных литералов ( JLS 3.10.1 ) гласит:
Самый большой десятичный литерал типа int равен 2147483648
(2 31 ). Все десятичные литералы от 0
до 2147483647
могут появляться везде, где может появляться литерал int, но литерал 2147483648
может появляться только как операнд унарного оператора отрицания -
.
Итак ...
Первое утверждение - это присвоение допустимого целочисленного литерального значения. Нет ошибки компиляции.
Вторым оператором является ошибка компиляции, поскольку 2147483648
не предшествует унарный оператор отрицания.
Третье выражение не содержит целочисленного литерала, выходящего за пределы допустимого диапазона, поэтому с этой точки зрения это не ошибка компиляции.
Вместо этого третье выражение является двоичным выражением сложения, как описано в JLS 15.18.2 . Это заявляет следующее о целочисленном регистре:
Если целочисленное сложение переполняется, то результатом являются младшие биты математической суммы, представленные в некотором достаточно большом формате с двумя дополнениями. Если происходит переполнение, то знак результата не совпадает со знаком математической суммы двух значений операнда.
Таким образом, 2147483647 + 1
переполняется и переходит в -2147483648
.
@ Питер Лори предлагает (легкомысленно?), Что третье утверждение может быть «переписано компилятором» как +2147483648
, что приведет к ошибке компиляции.
Это не правильно.
В JLS нет ничего, что говорило бы, что константное выражение может иметь значение, отличное от непостоянного выражения. Напротив, в случаях, подобных 1 / 0
, JLS переворачивает и говорит, что выражение НЕ является константным выражением, ПОТОМУ ЧТО оно заканчивается ненормально. (Находится в JLS 15,28 )
JLS очень старается избегать случаев, когда некоторые Java-конструкции означают разные вещи, в зависимости от компилятора. Например, это очень строго относится к правилам «определенного присваивания», чтобы избежать случая, когда только умный компилятор может определить, что переменная всегда инициализируется перед ее использованием. Это хорошая вещь с точки зрения переносимости кода.
Единственная существенная область, где есть «пространство для маневра» для разработчиков компиляторов для выполнения специфичных для платформы вещей, находится в областях параллелизма и модели памяти Java. И для этого есть веская прагматичная причина - позволить многопоточным приложениям Java быстро работать на многоядерном / многопроцессорном оборудовании.