This question already has answers here:
Java conditional operator ?: result type
(5 answers)
Closed 6 years ago.
I'm getting nullpointer exception if I use ternary operator.
Integer val = null;
Object res = val == null ? val : val.intValue();
But not with if else
Integer val = null;
Object res;
if( val == null ) {
res = val;
} else {
res = val.intValue();
}
Can anyone please explain why?
Thanks
Sudar
The behavior you encountered results from the rules of determining the type of the ternary conditional expression.
In your case, the type of the expression
val == null ? val : val.intValue();
is int.
This is specified by JLS 15.25. :
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
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.
Your second operand is Integer and your third operand is int, therefore the type of the expression is int.
Therefore, when val == null, val is un-boxed (i.e. val.intValue() is called for a null value) and a NullPointerException is thrown.
In your if-else expression val is not un-boxed when its value is null (since you assign it to an Object variable, so there's no NullPointerException.
That said, since you are assigning an Integer variable to an Object variable, your conditions in either of the snippets are pointless (since assigning an int to an Object variable simply boxes the int back to Integer).
You can simply assign
Object res = val;
and get the same end result without the exception.
Related
I have following code:
public String myMethod(String keyValue) {
Map<String, Integer> keyValueToRowIndex = ...
Integer rowIndex = (keyValue == null) ? 0 : keyValueToRowIndex.get(keyValue);
if (rowIndex == null)
return null;
...
}
Eclipse gives a "dead code" warning on the return null;. Removing the test for keyValue == null also removes the warning but I don't see how that extra test makes the return statement dead code. Clearly if the map contains no entry for some non-null keyValue, then rowIndex can still be null. Or am I missing something here?
I've seen similar Eclipse issues (here for instance), but this one seems a different and more trivial one.
The (surprising) short answer: Eclipse is right! This is dead code!
Reason
The important part is the ternary expression in the following line of code:
Integer rowIndex = (keyValue == null) ? 0 : keyValueToRowIndex.get(keyValue);
The Java language specification (JLS) says about the "Conditional Operator ?", that, if the first expression is of type int, and the second expression is of type Integer, the type of the entire expression will be int.
In your case, the first expression is the constant literal value 0, which is an int. The second expression is the result of the get method, which returns an object of type Integer. So according to the JLS, the entire expression has primitive type int!
This means, if the second expression (the get-call) will be evaluated, the result will be unboxed from Integer to int. This int value will then be auto-boxed again into an Integer to be able to assign it to the left operand, rowIndex.
But what happens, if the map returns a null value? In this case, unboxing from Integer to int is not possible, and a NullPointerExpression will be thrown!
So eclipse is right, as your expression can never return null, rowIndex will never be null either and the then-block of your if-statement will never be executed and hence is dead code!
Solution
The solution is simple: Use an Integer object instead of an primitive int value for your first expression:
Integer rowIndex = (keyValue == null) ? Integer.valueOf(0) : keyValueToRowIndex.get(keyValue);
My guess is that line 3 is interpreted as
Integer rowIndex = Integer.valueOf((keyValue == null) ? 0 : keyValueToRowIndex.get(keyValue).intValue());
(so both arguments to ?: are unified as int) - oddly enogh, Eclipse now shows no warning, even if it is now obvious that rowIndex is never null...
You might also replace the 0 with Integer.valueOf(0) to make the warning disappear.
This question already has answers here:
Why comparing Integer with int can throw NullPointerException in Java?
(7 answers)
Closed 5 years ago.
Could you please tell me why I get a NullPointerException here?
public class N {
private Integer n = null;
public static void main(String... wargh) {
N obj = new N();
System.out.println(obj.n == 1);
}
}
The obj.n is (obviously!) null here, so obj.n == 1 must return false - just the same way as null == 1 returns false. But it does not. Instead, it throws an exception.
null cannot be compared to a primitive, since a primitive can never be equal to null.
null == 1 doesn't return false - it doesn't pass compilation.
Comparing an Integer to an int requires unboxing of the Integer into an int. obj.n == 1 throws NullPointException when obj.n == null, since unboxing obj.n is equivalent to executing obj.n.intValue().
Because obj.n == 1 is interpreted as primitive integer comparison, so obj.n will be auto-unboxed calling out.n.intValue(), which leads to the exception.
Formally, JLS 15.21 makes it clear that compiler knows your code is a numeric comparison, and then JLS 15.21.1 applies which states:
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
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 ;)
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.
This question already has answers here:
NullPointerException through auto-boxing-behavior of Java ternary operator
(3 answers)
Closed 7 years ago.
I have the following return statement:
public Boolean foo(String booleanString){
return ("true".equals(booleanString) ? true : ("false".equals(booleanString) ? false : null));
}
when booleanString equal not true and not false I get the NullPointerException.
Is it boxing/unboxing issue?
You guessed it right. For a formal explanation, the answer lies in the JLS:
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.
So as you have the primitive true and false in both expressions, the type of your condition expression is boolean.
When you get into the second expression, in the second case, the null reference is converted into boolean with null.booleanValue();, causing the NPE, so that the expression is equivalent to:
return Boolean.valueOf(null.booleanValue());
(then the return type of the expression is re-boxed to Boolean, but it's too late as you guessed it).
For example:
return ("true".equals(booleanString) ? Boolean.TRUE : ("false".equals(booleanString) ? Boolean.FALSE : null));
does not cause a NPE since the type of the expression is Boolean. This, however,
return ("true".equals(booleanString) ? true : ("false".equals(booleanString) ? Boolean.FALSE : null));
causes it because again the same rule applies (since the first expression is the primitive boolean type). So it's equivalent to:
return Boolean.valueOf(("true".equals(booleanString) ? true : ("false".equals(booleanString) ? Boolean.FALSE : null).booleanValue());
As you are returning object type Boolean, then java tries to unbox return value null to a boolean primitive type in a logical expression, where foo() has been using. And you get Null Pointer Exception.
Here is a similar case and my explanation: https://stackoverflow.com/a/30055584/784540
I tripped across a really strange NullPointerException the other day caused by an unexpected type-cast in the ternary operator. Given this (useless exemplary) function:
Integer getNumber() {
return null;
}
I was expecting the following two code segments to be exactly identical after compilation:
Integer number;
if (condition) {
number = getNumber();
} else {
number = 0;
}
vs.
Integer number = (condition) ? getNumber() : 0;
.
Turns out, if condition is true, the if-statement works fine, while the ternary opration in the second code segment throws a NullPointerException. It seems as though the ternary operation has decided to type-cast both choices to int before auto-boxing the result back into an Integer!?! In fact, if I explicitly cast the 0 to Integer, the exception goes away. In other words:
Integer number = (condition) ? getNumber() : 0;
is not the same as:
Integer number = (condition) ? getNumber() : (Integer) 0;
.
So, it seems that there is a byte-code difference between the ternary operator and an equivalent if-else-statement (something I didn't expect). Which raises three questions: Why is there a difference? Is this a bug in the ternary implementation or is there a reason for the type cast? Given there is a difference, is the ternary operation more or less performant than an equivalent if-statement (I know, the difference can't be huge, but still)?
According to JLS: -
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional
expression.
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.
The problem is that:
Integer number = (condition) ? getNumber() : 0;
Forces an unboxing and reboxing of the result of getNumber(). This is because the false part of the ternary (0) is an integer, so it tries to convert the result of getNumber() to an int. Whereas the following does not:
Integer number = (condition) ? getNumber() : (Integer) 0;
This is not a bug, just the way Java chose to do things.
This is how it is supposed to work. The ternary operator is not meant to be equivalent to a regular if statement. The bodies of if and else are statements, while the parts following ? and : are expressions, that are required to evaluate to the same type.
Put another way: a = b ? c : d is not supposed to be equivalent to if (b) a = c; else a = d;. Instead, b ? c : d is an expression on its own, and the assignment of its result to a won't affect the outcome.