What is unary numeric promotion, and where does it differ from binary numeric promotion?
answer
- One operand -> int floor only
- Applies: +x, -x, ~x, shifts, array index
- Shift result type = left operand type
- Shift distance masked (low 5 bits int / 6 bits long)
- Array index promoted to int -> int-length cap
basics
~20 sUnary numeric promotion converts a single operand. If it is smaller than int (byte, short, char), it becomes int; otherwise it keeps its type. It applies to things like unary minus, the array index, bit shifts, and ~.
solid answer
~50 sUnary numeric promotion acts on a *single* operand rather than a pair. The rule is simpler than binary promotion: a byte, short, or char operand is widened to int; an operand already of type int, long, float, or double keeps its type. It is applied in contexts such as the operand of unary plus and minus (+x, -x), the bitwise complement (~x), each operand of a shift (the shift operators do NOT do binary promotion — each side is promoted independently, and the left side's type is the result type), array index expressions (the index is promoted to int, which is why arrays are capped at int length), and array dimension sizes. The crucial contrast: binary promotion finds a *common* type for two operands and the result can be long/float/double; unary promotion only lifts a single sub-int value to int. For shifts, -1 << 1L still produces an int because the left operand governs the result type and is unary-promoted on its own.
code
java · 10 linesbyte b = 5;
// byte n = -b; // ERROR: -b is an int
int n = -b; // OK: unary promotion to int
long r = -1 << 1L; // int -2, widened to long on assignment
int shifted = 1 << 33; // shift by 33 & 31 = 1 -> 2 (distance masked)
int[] arr = new int[3];
short idx = 1;
int v = arr[idx]; // idx promoted to int for the indexgo deeper
Recognizes that operations like -b on a byte produce an int, so a cast is needed to store it back.
Lists the unary contexts (+, -, ~, shifts, array index) and states the int-only widening rule.
Explains the shift special-case (left-operand result type, masked distance) and why array indices are int, contrasting clearly with binary promotion.
Can connect these rules to JVM bytecode (ishl/lshl, distance masking), the design rationale behind int-indexed arrays, and large-collection workarounds.
## Two flavors of promotion Java defines two related rules. **Binary numeric promotion** (covered separately) takes the *two* operands of an operator and converts them to a single shared type, where the result may be `int`, `long`, `float`, or `double`. **Unary numeric promotion** takes a *single* operand and applies a much simpler rule: > If the operand is `byte`, `short`, or `char`, convert it to `int`. Otherwise (`int`, `long`, `float`, `double`), leave it as-is. There is no 'find a common type' step — there's only one operand. The only thing unary promotion ever does is lift a sub-`int` integral type up to `int`. ## Where unary promotion applies (per the JLS) 1. **Unary plus and minus**: `+x` and `-x`. So if `byte b = 5;`, then `-b` is an `int`. `byte c = -b;` won't compile without a cast. 2. **Bitwise complement** `~x`: `~b` for a byte is an `int`. 3. **Each operand of a shift** (`<<`, `>>`, `>>>`): shifts are special — they do **not** use binary promotion. Each operand is **unary-promoted independently**, and the **type of the result is the (promoted) type of the LEFT operand**. The right operand's type does not influence the result type; only its low bits are used as the shift distance (low 5 bits for int, low 6 bits for long). So `1 << 40` shifts by `40 & 31 = 8`, and `(byte)1 << 1` is an `int`. 4. **Array index**: in `a[i]`, the index `i` is unary-promoted to `int`. This is why Java arrays cannot be indexed by `long` and are limited to `Integer.MAX_VALUE` elements. 5. **Array creation dimension sizes**: `new int[n]` promotes `n` to `int`. ## The key contrasts with binary promotion | Aspect | Unary | Binary | |---|---|---| | Operands | one | two | | Result can be long/float/double? | only if the operand already was | yes, by widening the other operand | | Finds a 'common' type? | no | yes | | Example | `-byteVar` -> int | `byteVar + longVar` -> long | A subtle consequence: in `byteVar + longVar`, binary promotion makes the byte a `long`. But in `-byteVar`, unary promotion only makes it an `int` — there is no second operand to force a wider type. ## Shift example that surprises people ```java long r = -1 << 1L; // result is int! ``` The left operand `-1` is `int`; unary promotion leaves it `int`; the result type is `int`. The right operand `1L` is unary-promoted to `long` independently but only its low bits are used as the distance; it does NOT widen the result to `long`. So `-1 << 1L` is the `int` `-2`, then widened to `long` on assignment. ## Deriving answers For any single-operand context, ask: is the operand smaller than int? If yes -> int; if no -> unchanged. For shifts, additionally remember the result follows the left operand and the distance is masked.
- Why is '-1 << 1L' of type int even though the right operand is long?Shift operators unary-promote each operand separately and the result type is the left operand's promoted type (int here). The right operand only supplies the shift distance via its low bits and never widens the result.
- Why can't a Java array be indexed with a long?The array-access index expression is subject to unary numeric promotion to int. There is no promotion path to long, so indices are int, which also caps array length at Integer.MAX_VALUE.
saying these in an interview costs you the question
- Saying shifts use binary promotion and the right operand widens the result
- Thinking -byteVar stays a byte
- Believing the array index can be a long
- Assuming unary promotion can produce long/float/double from a smaller type