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
Related
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.
This question already has answers here:
Unwanted NullPointerException in ternary operator - Why? [duplicate]
(2 answers)
Nullpointer exception with conditional operator, (ternary operator) but not with if else [duplicate]
(1 answer)
Closed 3 years ago.
To my understanding following code should not throw Null Pointer exception as I am safely using Optional interface.
However, when I ran this code it is throwing NPE.
public class Test {
public static void main(String[] args) {
final Integer inte = false ? 0 : Optional.ofNullable((Integer) null).orElse(null);
}
}
Please let me know if I am mistaken somewhere in my code and help me to rectify the same.
The reason you get a NullPointerException, is that the type of the expression false ? 0 : Optional.ofNullable((Integer) null).orElse(null) is int (by JLS Table 15.25-C).
The expression Optional.ofNullable((Integer) null).orElse(null) evaluates to null, and casting a null to an int results in a NullPointerException.
Surely you'll get a NullPointerException, because your right hand side is just a very verboose way of saying null. You're wrapping a null in an Optional thus forcing the orElse which executes to null.
Having null in a statement that resolves to int, as explained by #Hoopje (even if it's assigned to an Integer variable) leads to a NullPointerException.
Just wrapping null in an Optional still gives you a null when unwrapping it again.
I found the work around
final Integer inte = false ? (Integer)0 : Optional.<Integer>ofNullable(null).orElse(null);
or
final Integer inte = false ? (Integer)0 : Optional.ofNullable((Integer)null).orElse(null);
the type returned by the ternary operator is expected to be int (because of the literal 0).
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:
Returning null as an int permitted with ternary operator but not if statement
(8 answers)
Strange Java behaviour. Ternary operator
(1 answer)
Closed 4 years ago.
Given the following code:
boolean c = true;
boolean d = true;
boolean b = c ? null : d;
System.out.println(b);
Why does the compiler not complain here?
Variable b is a primitive datatype, shouldn't the null produce an error message like "Type mismatch: cannot convert from null to boolean"?
My best guess is, that there's some autoboxing going on?
I saw this code in a project, but I would love to know the exact reason behind this...
EDIT1:
As noted below by Mena, this code produces a NullPointer during runtime
EDIT 2:
the following form also compiles without error:
boolean c = false;
boolean d = true;
boolean b = c ? null : d;
System.out.println(b);
EDIT 3:
When trying to compile with compiler level 1.4, this will NOT compile, but produce an error:
Incompatible conditional operand types null and boolean.
So auto-boxing would make sense, as it was introduced with 1.5?
The expression on the RHS is of type Boolean and will be auto-unboxed at runtime; compile time type checking won't be affected. The unboxing will lead to a runtime exception.
This does not compile, since we can not assign the null value to a variable of primitive type
boolean e = null;
This also does not compile, although there is a decision, the compiler detects that the value is always null and the same as the previous case
boolean f = c ? null : null;
In this case, as the compiler does not know the final value of b then the code compiles, but fails to run because finally the value is null
boolean c = true;
boolean d = true;
boolean b = c ? null : d;
The types in the ternary expressions have to be of the same type, so I guess the JLS says that the types are auto-boxed in such a case (it will become Boolean); considering how little checks/optimizations javac does, this is not done here. It's interesting that intellij does complain for example, that a potential NullPointerException will be thrown.
To me, this somehow falls in the same category of:
String s = null;
if (true == true) {
}
if(s == null) {
}
and the like... they all are known at compile time for us, but not for the compiler.
A little bit un-related, but a ternary operator is far from an if statement, Holger once showed me this awesome example with promotion:
boolean b = true;
Object result = b ? Integer.valueOf(42) : Long.valueOf(12);
System.out.println(result.getClass() + " " + result); // class java.lang.Long 42
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.