This behaviour is described in the language spec.
Case 1 and 3 are described by the same point:
If the second and third operands have the same type, then that is the type of the conditional expression.
In case 1, the operands are of type int, so the overall expression is of type int, so it is incompatible. In case 3, the operands of of type byte, hence the result is compatible.
Case 2 is surprising to me: I would have expected that to fail as well, because the int operand would cause the conditional expression to be of int type.
However, this behaviour is described in the following point:
If one of the operands is of type T where Tis byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.
The 20 is a constant expression which fits in byte, hence the result is a byte.
Case 4 is also described by the "operands of the same type rule" used for cases 1 and 3; however, the fact that the condition is now constant makes it a constant expressions.
A constant expressions of int type is implicitly narrowed when assigning to a variable of narrower type, as described in Assignment contexts:
A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.
int
tofinal int
(constant) the compiler will see that the values ofa
andb
are in the byte range -128..127. For int variables the compiler does no such evaluation and assumes the int could overflow the byte range. – Egret