Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Wrong" return type when using if vs. ternary opertator in Java

In the following class, the return type of the two methods is inconsistent with the idea that the ternary operator:

return condition?a:b;

is equivalent to

if(condition) {
    return a;
} else{ 
    return b;
}

The first returns a Double and the second a Long:

public class IfTest {
    public static Long longValue = 1l;
    public static Double doubleValue = null;

    public static void main(String[] args) {
        System.out.println(getWithIf().getClass());// outpus Long
        System.out.println(getWithQuestionMark().getClass());// outputs Double
    }

    public static Object getWithQuestionMark() {
        return doubleValue == null ? longValue : doubleValue;
    }

    public static Object getWithIf() {
        if (doubleValue == null) {
            return longValue;
         } else {
            return doubleValue;
        }
    }
}

I can imagine this has to do with the compiler narrow casting the return type of getWithQuestionMark() but is that language wise ok? It's certainly not what I would have expected.

Any insights most welcome!

Edit: there's very good answers below. Additionally, the following question referenced by @sakthisundar explores another side effect of the type promotion occurring in the ternary operator: Tricky ternary operator in Java - autoboxing

like image 343
Miquel Avatar asked Aug 24 '12 11:08

Miquel


People also ask

Can we use ternary operator in if condition in Java?

Java ternary operator is the only conditional operator that takes three operands. It's a one-liner replacement for the if-then-else statement and is used a lot in Java programming. We can use the ternary operator in place of if-else conditions or even switch conditions using nested ternary operators.

What is the return type of ternary operator in Java?

If the first operand is true then java ternary operator returns second operand else it returns third operand. Syntax of java ternary operator is: result = testStatement ? value1 : value2; If testStatement is true then value1 is assigned to result variable else value2 is assigned to result variable.

Which is faster ternary operator or if else?

Moreover, as has been pointed out, at the byte code level there's really no difference between the ternary operator and if-then-else.


2 Answers

Basically it's following the rules of section 15.25 of the JLS, specifically:

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.

So section 5.6.2 is followed, which will basically involves unboxing - so this makes your expression work as if longValue and doubleValue were of types long and double respectively, and the widening promotion is applied to the long to get an overall result type of double.

That double is then boxed in order to return an Object from the method.

like image 118
Jon Skeet Avatar answered Nov 15 '22 15:11

Jon Skeet


In addition to @Jon's answer, looking at the bytecode you see:

public static java.lang.Object getWithQuestionMark();
  Code:
   0:   getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   3:   ifnonnull       16
   6:   getstatic       #8; //Field longValue:Ljava/lang/Long;
   9:   invokevirtual   #9; //Method java/lang/Long.longValue:()J
   12:  l2d
   13:  goto    22
   16:  getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   19:  invokevirtual   #10; //Method java/lang/Double.doubleValue:()D
   22:  invokestatic    #11; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
   25:  astore_0
   26:  aload_0
   27:  areturn

Whereas if you tell the compiler that you're not interested in numbers:

public static Object getWithQuestionMark() {
    return doubleValue == null ? (Object)longValue : (Object)doubleValue;
}

you'll get what you were after (bytecode)

public static java.lang.Object getWithQuestionMark();
  Code:
   0:   getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   3:   ifnonnull       12
   6:   getstatic       #8; //Field longValue:Ljava/lang/Long;
   9:   goto    15
   12:  getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   15:  areturn

outputs:

$ java IfTest
class java.lang.Long
class java.lang.Long
like image 32
beny23 Avatar answered Nov 15 '22 14:11

beny23