Here is an explanation from the point of view of the Java language specification.
The section on integer literals ( JLS 3.10.1 ) says the following:
The largest decimal literal of type int is 2147483648 (2 31 ). All decimal literals from 0 to 2147483647 can appear wherever int literal can be displayed, but the literal 2147483648 can only be displayed as an operand of the unary negation operator - .
So...
The first statement is the assignment of a left integer literal value. Compilation error.
The second statement is a compilation error, because 2147483648 not preceded by a unary negation operator.
The third statement does not contain an integer literal that is out of range, therefore, from this point of view, this is not a compilation error.
Instead, the third statement is a binary addition, as described in JLS 15.18.2 . This expresses the following about the integer case:
If the integer addition overflows, then the result is the least significant bits of the mathematical sum, presented in some fairly large format with two additions. If overflow occurs, the sign of the result does not coincide with the sign of the mathematical sum of the two values of the operand.
Thus, 2147483647 + 1 overflows and wraps up to -2147483648 .
@Peter Lawrey suggests (frivolously?) That the third statement can be "rewritten by the compiler" as +2147483648 , which leads to a compilation error.
This is not true.
There is nothing in JLS that suggests that a constant expression may have a different meaning for a mutable expression. In contrast, in cases like 1/0, JLS flips things around and says that the expression is NOT a BECAUSE constant expression because it aborts abnormally. (It is in JLS 15.28 )
JLS tries very hard to avoid cases where some kind of Java construct means different things, depending on the compiler. For example, it is very important to follow the rules of a “specific purpose” in order to avoid the case when only an intelligent compiler can infer that a variable is always initialized before it is used. This is a good idea in terms of code portability.
The only significant area where there is “room for maneuver” for compiler developers to perform specific platform tasks is in the concurrency and Java memory models. And there is a good pragmatic reason for this - to allow multi-threaded Java applications to quickly work on multi-core / multi-processor equipment.