Surprised by a promotion

24 November 2015

Each week I receive the weekly email from Java Code Geeks and almost all the time I spent few minutes checking what’s going on in the programming world. Last week they grab my attention much better than the other times, who doesn’t want to read 10 Things You Didn’t Know About Java? The entire post is worth reading but for me the most mind blowing part was the section about conditional expression. Why? Because it looks so simple on the surface but it does more than we think.

Conditional expression

In the most simple form it looks like:

1

2

result=condition?value1:value2

and I saw it used inlined in expressions where the code is still readable:

1

2

returncondition?value1:value2

It looks pretty simple, the code is clear, what could go wrong?

What do you think the code bellow will display?

1

2

3

4

Objecto1=true?newInteger(1):newDouble(2.0);

System.out.println(o1);

System.out.println(o1.getClass());

The condition is always true, you could say that the result is:

1

2

3

1

classjava.lang.Integer

but if you run the code you will see:

1

2

3

1.0

classjava.lang.Double

Why on earth is this happening? It is because you received a promotion, but not the one you are expecting for. If you check Conditional operator specification, you’ll find that in our case the result type of this conditional expression is bnp(Integer,Double) where bnp(..) means to apply binary numeric promotion. If it is still confusing, check Binary Numeric Promotion and it will be clear:

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.

We have unboxing from Integer to int, widening primitive conversion from int do double and boxing from double to Double. Please don’t go, there is another surprise here. Usually the operands are the results of other expressions and because they are references they can be null also:

1

2

3

4

5

6

Integervalue1=newInteger(1);

Doublevalue2=newDouble(2.0);

if(true)value1=null;

Objecto1=true?value1:value2;//line 8

System.out.println(o1);

You could say that null will be printed but you’ll be again surprised by

1

2

3

Exception inthread"main"java.lang.NullPointerException

at Test.main(Test.java:8)

The reason is that before widening primitive conversion, we have unboxing from Integer to int, in our case from null to int.

I never thought that a simple expression like this can cause so much trouble. It is the proof that strong essences are kept in small bottles.