Java: Integers may overflow, but bytes may not?
Why are int
expressions allowed to overflow, but not byte
expressions?
int i = Integer.MAX_VALUE + 1; // allowed to overflow
byte b = possible lossy conversion from int to byteByte.MAX_VALUE + 1; // not allowed to overflow
Before you shout "Byte.MAX_VALUE + 1
is an int
expression! This is a simple type error!" note that this does compile:
byte b = Byte.MAX_VALUE + 0;
The + Operator
The +
operator is indeed only defined for int
, long
, float
, double
and String
operands. In our snippet Byte.MAX_VALUE
will therefore be promoted to an int
and the result of the addition will be of type int
.
Example: A byte
plus a byte
is an int
:
String type(byte b) { return "byte"; }
String type(int i) { return "int"; }
…
byte a = 10, b = 20;
System.out.println(type(a + b)); // "int"
But as we saw earlier, there's something else at play here…
Something about that overflow…
The compile error only occurs when there's a byte overflow, which reveals that the computation is performed at compile time. The JLS refers to this as a compile-time constant expression:
15.28 Constant Expression
A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:
- …
- Literals of primitive type and literals of type String (§3.10.5)
- The additive operators + and -
- Qualified names of the form TypeName.Identifier that refer to constant variables (§4.12.4) JLS §15.28
A constant variable is a variable that is final and initialized with a compile-time constant expression. Byte.MAX_VALUE
happens to be a constant variable. (If it wasn’t byte b = Byte.MAX_VALUE + 0
would not have compiled!)
Example: Making a variable final makes it a constant expression.
Why is a byte not allowed to overflow like an int?
There are some special language rules for so called assignment contexts. Constant expressions that fit in the type of the variable (byte
in our case) will be automatically converted by the compiler.
5.2. Assignment Context
[…]
A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable. JLS §5.2
So here’s the actual reason the snippet at the top doesn’t compile: Constant expressions that do not fit does not get converted, and thus the expression remains as an int
and a typing error occurs since an int
cannot be stored in a byte
variable.
An even funnier case
During evaluation of constant expressions, the compiler follows the rules of 2’s complement 2 arithmetic. This means that an int
expression can overflow and wrap back into the range of a byte
!
byte a = Integer.MAX_VALUE; // Does not compile.
byte b = Integer.MAX_VALUE * 2; // Does compile!
The b
variable will contain the value −2.
Similar article
There are literals for all primitive types (int
, long
, char
, float
, double
, char
, and boolean
) except byte
and short
. No byte or short literals? discusses this oddity.