Recently I had a little surprise. It started me compiling some Java class and it did not work the way I expected. This is not the surprise itself: this is just the way I live. Develop, debug, release cycles. So the development cycle started. I was looking to find a bug in my code and as I tightened the bug boundary it came down to a ternary operator. The following sample demonstrates the situation:

public class Bug{
    public static Number q(Number in) {
        return in instanceof Long ? 1L : 1.0;
    }
}

It was supposed to return a Long or a Double value of one, depending on the type of the argument. However the method returned a 1.0 Double.

I looked at the code generated by javac using the javap disassembler and I saw that the case is really the one I explained above:

public static java.lang.Number q(java.lang.Number);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: instanceof    #2                  // class java/lang/Long
         4: ifeq          11
         7: dconst_1
         8: goto          12
        11: dconst_1
        12: invokestatic  #3                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
        15: areturn

No matter what type the argument has, the code loads a constant Double on the operational stack executing the bytecode (dconst_1). This code is not good. But it does not mean that there is a bug in the javac compiler. This is not a bug, this is a feature.

The next thing I consulted was the Java language specification. You can read it from the URL http://docs.oracle.com/javase/specs/jls/se7/html/index.html and it talks about the ternary operator:

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.

In our case numeric promotion happens according to §5.6.2 :

If either operand is of type double, the other is converted to double.

It explains why this code above is useless. in situations like that use the good old 'if' 'then' 'else' construct.

1.1.1. Update

Recently Lukas Eder posted a similar article on his JOOQ blog worth following. This article is brief and highlights a different effect of the same issue.

Comments imported from Wordpress

Tyborg 2013-10-10 10:07:20

You can cast it to Number, and this will work well: return in instanceof Long ? (Number) 1L : (Number) 1.0;


Comments

Please leave your comments using Disqus, or just press one of the happy faces. If for any reason you do not want to leave a comment here, you can still create a Github ticket.