Stock Ledger: Narrowing, Compound Assignment and Ternary Unboxing
A warehouse team is rewriting its stock ledger in a Java SE 8 codebase. The class holds short onHand = 120; and byte reorder = 10;. The restock method contains three statements: onHand = onHand + 40;, onHand += 40; and reorder *= 2.5;. The build server reports exactly one compile error, yet a teammate insists two of the three lines cannot compile because a double and an int are being assigned to smaller types. Separately, the picking method takes int requested and Integer available and computes int qty = available == null ? 0 : available;, then Integer packed = requested > 0 ? requested : null;, then int total = packed;. Staging tests pass, but production logs a NullPointerException on the line that assigns total. Finally, a switch on a char grade has case 'A': and case 'B': sharing one statement with no break before case 'C':, and the reviewers disagree about the bonus owed to grades 'A' and 'B'.
1. Which of the three restock statements fails to compile, and why do the other two succeed despite the apparently lossy conversions?
Read the worked answer
Only onHand = onHand + 40; fails.
Why it fails: binary numeric promotion converts both operands of + to int, so the result is an int. Assigning an int expression to a short variable requires an explicit cast; assignment conversion only narrows automatically for constant expressions that fit (a final/literal value), not for an expression involving a variable.
Why onHand += 40; compiles: a compound assignment E1 op= E2 is defined as E1 = (T)((E1) op (E2)), where T is the type of E1. The cast back to short is inserted implicitly, so the line is legal and onHand becomes 160.
Why reorder *= 2.5; compiles: it is equivalent to reorder = (byte)(reorder * 2.5). The multiplication happens in double (25.0) and the implicit cast narrows it to byte 25. It compiles; the risk is silent truncation, not a compile error. Note the contrast with reorder = reorder * 2.5;, which would not compile.
So exactly one compile error is consistent with the build log, and the teammate's 'two errors' theory is wrong because compound assignment hides the narrowing cast.
2. Explain why `int qty = available == null ? 0 : available;` is safe when `available` is null, while `int total = packed;` throws NullPointerException.
Read the worked answer
Both involve unboxing, but at different moments.
Ternary type: when one operand of ?: is a primitive int (here the literal 0) and the other is an Integer, binary numeric promotion applies and the whole conditional expression has type int. That means the Integer branch is unboxed when it is evaluated. Crucially, only one branch is evaluated: when available == null the condition is true, the 0 branch is chosen, and the available branch is never evaluated, so no unboxing of null occurs. Result: qty is 0.
Assignment to packed: requested > 0 ? requested : null has type Integer (int mixes with the null type by boxing), so no unboxing happens there. The failure is on the next line: int total = packed; performs an implicit packed.intValue() on whatever reference packed holds. When requested <= 0, packed is null, so the unboxing dereferences null and throws NullPointerException at runtime.
The fix is to guard the unboxing, e.g. int total = packed == null ? 0 : packed;, or keep the value in an Integer and test before use.
3. With the switch below, what total does each grade receive? Explain the control flow. ```java switch (grade) { case 'A': case 'B': totalUnits += 5; case 'C': totalUnits += 3; break; default: totalUnits += 1; } ```
Read the worked answer
Grade 'A' and 'B' both get +8, 'C' gets +3, and any other char gets +1.
Reasoning: a switch on char is legal (byte, short, char, int, their wrappers via unboxing, String and enum are permitted). Case labels must be compile-time constants and must be unique.
'A': labels'A'and'B'are stacked on the same statement, sototalUnits += 5runs, and because there is nobreak, execution falls through intocase 'C':and adds 3 more, then hitsbreak.'B': jumps to the same statement, adds 5, falls through, adds 3 - identical to 'A'.'C': starts at its own label, adds only 3, then breaks.default: adds 1. The position ofdefaultdoes not matter; it runs only when no case matches (and it also falls through if it is placed before a matching case label without abreak).
The reviewers who expect 'A' to receive only +5 are ignoring fall-through. Adding break; after totalUnits += 5; would isolate 'A'/'B' at +5.