Run the following Java code:
boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;
Why is there a NullPointerException?
The return type of the conditional expression b ? d1.doubleValue : d2 is double. A conditional expression must have a single return type. Following the rules for binary numeric promotion, d2 is autounboxed to a double, which causes a NullPointerException when d2 == null.
From the language spec, section §15.25:
Otherwise, if the second and third
operands have types that are
convertible (§5.1.8) to numeric types,
then there are several cases: ...
Otherwise, binary numeric promotion (§5.6.2) is applied
to the operand types, and the type of
the conditional expression is the
promoted type of the second and third
operands. Note that binary numeric
promotion performs unboxing conversion
(§5.1.8) and value set conversion
(§5.1.13).
Because the two expressions around : must return the same type. This means Java tries to convert the expression d2 to double. This means the bytecode calls doubleValue() on d2 -> NPE.
You should generally avoid mixed type computation; compounding this with ?: conditional/ternary only makes it worse.
Here's a quote from Java Puzzlers, Puzzle 8: Dos Equis:
Mixed-type computation can be confusing. Nowhere is this more apparent than conditional expression. [...]
The rules for determining the result type of a conditional expression are too long and complex to reproduce in their entirety, but here are three key points.
If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representible in type T, the type of the conditional expression is T.
Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Point 3 is applied here, and it resulted in unboxing. When you unbox null, naturally a NullPointerException is thrown.
Here's another example of mixed-type computation and ?: that may be surprising:
Number n = true ? Integer.valueOf(1) : Double.valueOf(2);
System.out.println(n); // "1.0"
System.out.println(n instanceof Integer); // "false"
System.out.println(n instanceof Double); // "true"
Mixed-type computation is the subject of at least 3 Java Puzzlers.
In closing, here's what Java Puzzlers prescribes:
4.1. Mixed-type computations are confusing
Prescription: Avoid mixed-type computations.
When using the ?: operator with numeric operands, use the same numeric type for both the second and third operands.
On prefering primitive types to boxed primitives
Here's a quote from Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives:
In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.
There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.
Related questions
What is the difference between an int and an Integer in Java/C#?
Why does autoboxing in Java allow me to have 3 possible values for a boolean?
Is it guaranteed that new Integer(i) == i in Java? (YES!!!)
When comparing two Integers in Java does auto-unboxing occur? (NO!!!)
Java noob: generics over objects only? (yes, unfortunately)
Why does int num = Integer.getInteger(“123”) throw NullPointerException?
Return same type for both condition like below and you will get result.
boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1 : (Double)d2;
System.out.println(d);
Related
byte a=10;
byte b=20;
b=a+b;
In this case, I need to explicitly convert a+b to byte like this :
b=(byte)(a+b);
It's the same with short :
short x=23;
short y=24;
Otherwise it gives an error.
But in case of integers, it's not required to convert explicitly :
int p=7788;
int q=7668;
p=p+q;
This will work just fine.
Why is that?
We don't need to explicitly cast even in the case of long as well.
If you look at the JLS 4.2.2 Integer Operations, it states that the result of a numerical operation between two integral operands is an int or a long. Since there's no implicit cast from an int to byte or a short, you need an explicit cast.
If you refer to JLS Sec 15.18.2, which deals with addition of numeric types, it says:
Binary numeric promotion is performed on the operands (§5.6.2).
...
The type of an additive expression on numeric operands is the promoted type of its operands.
JLS Sec 5.6.2 describes binary numeric promotion:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted to float.
Otherwise, if either operand is of type long, the other is converted to long.
Otherwise, both operands are converted to type int.
So, in the case of int and long (where both operands are of that type), binary numeric promotion is a no-op: the operands remain int and long respectively, and the result of the addition is int and long respectively, meaning the result can be assigned to variables of that type.
In the case of byte and short, binary numeric promotion causes both of those to be widened to int to perform the addition, and the result of the addition is int; you have to explicitly cast back again to the narrower type, because not all int values fit into a byte or short.
There are 2 exceptions to this requirement to do an explicit narrowing cast.
Firstly, compound assignments: this would have worked:
b += a;
because, as stated in JLS Sec 15.26.2:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
In other words: the compiler inserts the cast for you:
b = (byte) ((b) + (a));
Secondly, if the operands have constant values, and the result of the addition is known to fit into the range of the narrower type, and you are doing the assignment at the variable declaration.
For example:
final byte a=10; // final is necessary for a and b to be constant expressions.
final byte b=20;
byte c = a + b;
This requires no cast.
With addition in java, java will promote the smaller data type to the larger one. And when the data type is smaller than int it will promote both the operant to int. Take a look at: Promotion in Java?
This question already has answers here:
Unexpected type resulting from the ternary operator
(4 answers)
Closed 8 years ago.
Object myObject = true ? new Integer(25) : new Double(25.0);
System.out.println(myObject);
Strangely, it outputs 25.0 instead of 25
Whats going on?
Your code returns the second operand (new Integer(25)) as you expected, but it converts it to a Double due to the following rules.
Here's what JLS 15.25 says :
if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.
If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a constant expression (§15.28) of type int whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the conditional expression is U.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
And the numeric promotion :
5.6.2. Binary Numeric Promotion
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
In your example, you have an Integer and a Double. They are unboxed to int and double and then the int is converted to a double.
There is some wierd autoboxing stuff going on - you can see it better if you use different numbers:
Object myObject = true ? new Integer(25) : new Double(22.0);
Now, myObject will still be assigned a Double(25.0), not the 22.0 you would expect if the conditional didn't work. Basically, because Java thinks you are doing some sort of calculation involving an int and a double it returns the result of the iif as a "double" primative and then autoboxes it back to a Double().
You could also get it to behave as expected by forcing it to treat the values as type Object():
Object myObject = true ? (Object) new Integer(25) : (Object) new Double(22.0);
After compilation
Object myObject = true ? new Integer(25) : new Double(25.0);
will be something as below
Object myObject = (double) new Integer(25);
So, it returns new Integer(25) as expected but it gets converted to double.
As Eran mentioned, as per JLS 5.6.2:
binary numeric promotion (§5.6.2) is applied to the operand types, and
the type of the conditional expression is the promoted type of the
second and third operands.
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted
to float.
Otherwise, if either operand is of type long, the other is converted
to long.
Otherwise, both operands are converted to type int.
I have two variables that should be compared:
Double a = 1D;
Double b = 2D;
if (a > b) {
System.out.print("Ok");
}
In this case java will use autoboxing or compare two object's references?
From section 15.20.1 of the JLS:
The type of each of the operands of a numerical comparison operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs. Binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then signed integer comparison is performed; if this promoted type is float or double, then floating-point comparison is performed.
Section 5.6.2 starts with:
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order, using widening conversion (§5.1.2) to convert operands as necessary:
If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed.
So yes, unboxing is performed. > has no meaning for references themselves.
More interesting is the == case where both options would be possible - and in that case, if either operand is a primitive and the other can be converted via numeric promotion, then that happens... but if both are reference types, the reference comparison is performed. For example:
Double d1 = new Double(1.0);
Double d2 = new Double(1.0);
System.out.println(d1 == d2); // Prints false due to reference comparison
It will use autoboxing. You cannot do greater than on references
A simple expression:
Object val = true ? 1l : 0.5;
What type is val? Well, logically, val should be a Long object with value 1. But Java thinks that val is a Double with value 1.0.
It doesn't have to to anything with autoboxing as
Object val = true ? new Long(1) : new Double(0.5);
results with same behavior.
Just to clarify:
Object val = true ? "1" : 0.5;
results in the correct String.
Can anybody explain me why they have defined this like that? For me it seems to be rather really bad designed or actually a bug.
This is described in the Java Language Specification Section 15.25 (relevant parts in bold face):
The type of a conditional expression is determined as follows:
If the second and third operands have the same type [...]
If one of the second and third operands is of type boolean [...]
If one of the second and third operands is of the null type [...]
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte [...]
If one of the operands is of type T where T is byte, short, or char, [...]
If one of the operands is of type Byte [...]
If one of the operands is of type Short [...]
If one of the operands is of type; Character [...]
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
The "lub" mentioned in the last paragraph stands for least upper bound and refers to the most specific super type common for T1 and T2.
As for the case with Object val = true ? 1l : 0.5; I agree that it would be more precise if it applied rule 5 (on the boxed values). I guess the rules would become ambiguous (or even more complicated) when taking autoboxing into account. What type would for instance the expression b ? new Byte(0) : 0.5 have?
You can however force it to use rule 5 by doing
Object val = false ? (Number) 1L : .5;
Because the expression needs to be of a type.
If you do 1l + 0.5d, you end with 1.5d because the compiler changes automatically the types to the one which can allow all possible results.
In your case, the compiler sees ? and assigns the result of the expression the double using the same rule (you can write a long as a double, but not all doubles as longs).
That's not a design flaw:
The compiler has to determine the type of the entire expression true ? 1l : 0.5. It tries using a type where as few conversions as possible are required.
As 0.5 does not fit into a long (without loss of precision), the result of the expression can't be long - therefore, it has to be double and the long is simply converted (no loss of precision).
Note that you can't have an expression conditionally having different types - a tertiary expression has to evaulate to the same type regardless of the condition.
In the second snippet, Object is the only compatible type - the Double gets boxed and the String can be simply assigned.
When you write
Object val = true ? new Long(1) : new Double(0.5);
you need to consider that
(new Long(1) : new Double(0.5))
needs to have a value... The compiler needs to come up with a type that covers both possible values. For your example, it was double.
I cannot provide you with a good reason, but the type of the expression is given by the Java Language Specification: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25
In the JLS all the numeric types are treated as special cases.
This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
Booleans, conditional operators and autoboxing
Java, Google Collections Library; problem with AbstractIterator?
The code below produces a NPE:
Integer test = null;
Integer test2 = true ? test : 0;
System.out.println(test2);
To correctly print out "null" without an exception requires this code:
Integer test = null;
Integer test2 = true ? test : (Integer)0;
System.out.println(test2);
It's obvious in the first example that "test" is being unboxed (converted to native int), but why? And why does changing the other expression in the ternary operator (as in the 2nd example) fix it? Can anyone provide some kind of narrative of exactly when, what, and why stuff in both of the examples gets boxed and unboxed?
From section 15.25 of the Java Language Specification:
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 type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
So it's following the final bullet, performing binary numeric promotion, which performs an unboxing conversion. So the type of the conditional operator expression is int, even though you're assigning it to an Integer. It's trying to perform the unboxing conversion on null, hence the exception.