This question already has an answer here:
-
Booleans, conditional operators and autoboxing
4 answers
I am getting NullPointerException in one instance below while its counterpart runs smooth.
public static void main(String[] args){
System.out.println(withTernary(null, null)); //Null Pointer
System.out.println(withIfElse(null, null)); //No Exception
}
private static Boolean withTernary(String val, Boolean defVal){
return val == null ? defVal : "true".equalsIgnoreCase(val);
}
private static Boolean withIfElse(String val, Boolean defVal){
if (val == null) return defVal;
else return "true".equalsIgnoreCase(val);
}
Online version
Online version with the lines in main
reversed, which outputs null
from withIfElse
and then fails in withTernary
.
I am using following java version
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)
Here's the relevant quote from the spec (§15.25.2):
Boolean conditional expressions are standalone expressions (§15.2).
The type of a boolean conditional expression is determined as follows:
If the second and third operands are both of type Boolean
, the conditional expression has type Boolean
.
Otherwise, the conditional expression has type boolean
.
Therefore, the overall expression's type is considered to be boolean
, and the Boolean
value is autounboxed, causing a NullPointerException
.
As mentioned in the comments, why doesn't the following raise an exception?
return val == null ? null : "true".equalsIgnoreCase(val);
Well, the above excerpt from the spec specifically only applies to boolean conditional expressions, which are specified here (§15.25):
If both the second and the third operand expressions are boolean expressions, the conditional expression is a boolean conditional expression.
For the purpose of classifying a conditional, the following expressions are boolean expressions:
An expression of a standalone form (§15.2) that has type boolean
or Boolean
.
A parenthesized boolean
expression (§15.8.5).
A class instance creation expression (§15.9) for class Boolean
.
A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has return type boolean
or Boolean
.
(Note that, for a generic method, this is the type before instantiating the method's type arguments.)
A boolean
conditional expression.
Since null
is not a boolean expression, the overall conditional expression is not a boolean conditional expression. Referring to Table 15.2 (later in the same section), we can see that such an expression is considered to have a Boolean
type, so no unboxing occurs, and no exception is raised.
val == null ? defVal : "true".equalsIgnoreCase(val)
- in this expression, the third argument is boolean
, and since the ternary operator must have one static type, it will try to unbox the null
, hence the NPE. Only the assignment to Boolean
will cause boxing again.
Check this.
Because autounboxing. Java derive common type for defVal
and "true".equalsIgnoreCase(val)
. Type of first is Boolean, but second boolean. For unknown reason it thinks that common type will be boolean (You can find rule in spec).
This could be explained if defVal
is null
.
With ternary expressions, both options must be of exactly the same type and if not some coercion is applied:
The JLS 15.25 "Conditional operator ? :" says:
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
In the case of this expression:
val == null ? defVal : "true".equalsIgnoreCase(val)
The Boolean
value for defVal
is auto-unboxed to match the boolean
result of the string comparison. This is despite the fact that the result of the ternary is then auto-boxed back to a Boolean
- when deciding how to cast, a ternary does not consider anything outside itself.
Ok. Let's try in this way
public static void main(String[] args){
System.out.println(withTernary(null, null));
}
private static Boolean withTernary(String val, Boolean defVal){
return (val == null ? defVal : Boolean.valueOf("true".equalsIgnoreCase(val)));
}
Now it will works fine. Now there is no unboxing
here and will not give you exception.
But in other way due to null
unbox
you will get NPE
public static void main(String[] args){
System.out.println(withTernary(null, null)); //Null Pointer
}
private static Boolean withTernary(String val, Boolean defVal){
return (val == null ? defVal : "true".equalsIgnoreCase(val));
}