Just spent a frustrating couple of hours debugging this code:
LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
Integer boxedPci = 52;
Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;
The above produces a NullPointerException. The below code doesn't:
LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
Integer boxedPci = 52;
Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : Integer.valueOf(-1);
The only difference is wrapping the -1 with Integer.valueOf(). I'm sure I'm going to smack my forehead once somebody explains why this code behaves the way it does.. but can someone explain to me why this code behaves the way it does :)?
-- Edit
On second thought, I suspect that the NPE is coming from the rsrqs.get() returning null, which I think java is attempting to unbox into an int, before boxing back to an Integer. The Integer.valueOf() forces Java to do the unbox-box step. Moral of the story; don't just ignore those boxing warnings in Eclipse ;)
Overall, you should only use ternary statements when the resulting statement is short. Otherwise, write a normal if statement. The purpose of a ternary operator is to make your code more concise and readable. Moving a complex if statement into a ternary operator goes against that goal.
The syntax of the Java ternary operator is as follows: (condition) ? (return if true) : (return if false); You will often see the Java ternary operator symbols ( ? : ) used in texts and tutorials as an abbreviation for the construct.
As of Java 7, only one of the right-hand expressions of the ternary operator will be evaluated at runtime. In a manner similar to the short-circuit operators, if one of the two right-hand expressions in a ternary operator performs a side effect, then it may not be applied at runtime.
Hence with a Java 8 class library the result type of the ternary expression is Executable rather than Member . Some (pre-release) versions of the Java 8 compiler seem to have produced an explicit reference to Executable inside generated code when compiling the ternary operator.
Ternary expressions, like any expression, have a type that is determined by the compiler. If the two sides of the ternary expression have what looks like different types, then the compiler will try and find a common base type using the least ambiguous of the two options. In your case, the -1
is least ambiguous, and so the type of the ternary expression is int
. Sadly, the compiler doesn't use type inference based on the receiving variable.
The expression rsrqs.get(boxedPci.toString())
is then evaluated and forced into type int
to match the ternary expression, but because it's null
it throws the NPE.
By boxing the -1
, the value of the ternary expression is Integer
, and so you're null-safe.
The explanation can be concluded from the information in java language specification: 15.25. Conditional Operator ? :.
From the table there, you get the information, that, if the second operand (rsrqs.get(boxedPci.toString())
) is of type Integer
and the third operand is of type int
, the result will be of type int
.
That however means, that
Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;
is semantically the same as
Integer boxedRsrq = boxedPci != null ? ((int)rsrqs.get(boxedPci.toString())) : -1;
But that means you get a NullPointerException
, if you get null
from the map, which obviously happens.
If you cast the third operand to Integer
, the second operand will never be cast to int
and no NPE happens.
1
is an int, not an Integer. So, Java is going to un-box your Integer to int, which causes the NullPointerException. When you auto-unbox a null Integer, it results in a NullPointerException. ( reference here )
But when you use
Integer.valueOf(-1)
it doesn't need to auto-unbox it, which leads to no exceptions.
Well, Integer.valueOf(String)
returns an Integer
and -1 is a primitive int
. The first example is forced to unbox because one term is a primitive. You could also have used
Integer boxedRsrq = boxedPci != null ?
rsrqs.get(boxedPci.toString()) : (Integer) -1;
which would have boxed the -1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With