Mixed Wrapper Types In Ternary
ID |
java.dissimilar_primitive_wrappers_ternary_operator |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Java |
Tags |
reliability |
Description
Reports a ternary conditional expression (cond ? a : b) whose two branches box different
numeric wrapper types, such as Integer and Float. The compiler does not simply return
whichever branch the condition selects: it unboxes both branches and applies binary numeric
promotion to compute one common type for the whole expression.
Rationale
Integer, Long, Float, Double, Byte, Short and Character all satisfy the ternary’s
"numeric type" rule, so mixing any two of them forces the compiler to unbox both operands and
promote them to a common numeric type, even the one that was not selected by the condition. An
int promoted to float can silently lose precision once the value exceeds 2^24, and the
unboxing itself throws a NullPointerException if either boxed value is null — neither
consequence is visible at the call site.
Integer count = 123456789;
Float estimate = 1.0f;
// Bad -- both operands are unboxed and promoted to float, regardless of which one is picked
Number result = useEstimate ? estimate : count;
Remediation
Cast one branch to a common reference supertype (typically Number) to keep both values boxed
and skip the coercion, or, if the coercion was genuinely intended, cast explicitly to the target
primitive type so the conversion is visible in the code.
Integer count = 123456789;
Float estimate = 1.0f;
// Good -- explicit cast keeps the value boxed, no coercion happens
Number result = useEstimate ? estimate : (Number) count;
// Good -- explicit cast makes the intended coercion visible
float narrowed = useEstimate ? estimate : (float) count;