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.

final 
byte a = 1;
byte b = a + 1;
byte b = a + 1; // can't convert from int to byte

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.

Comments

Be the first to comment!