23

I had this piece of code in my application (simplified version):

Object result;
if (check)
    result = new Integer(1);
else
    result = new Double(1.0);
System.out.println(result);
return result;

Then I decided to refactor the if-else statement to a ternary conditional expression so my code is more concise:

Object result = check ? new Integer(1) : new Double(1.0);
System.out.println(result);
return result;

It turned out that in case check is true the two versions print different results:

1

or:

1.0

Isn't the ternary conditional equivalent to the corresponding if-else?

Random832
  • 37,415
  • 3
  • 44
  • 63
fkn
  • 487
  • 1
  • 3
  • 7
  • 5
    Something different is occurring in your code because given the code you have provided, the scenario you describe cannot happen. – JamesB May 16 '15 at 18:16
  • @JamesB sorry updated my question. Swapped 1.0 and 1 – fkn May 16 '15 at 18:24
  • 1
    @JamesB I can reproduce this O_o – Stefan Falk May 16 '15 at 18:26
  • @JamesB check is a parameter of a function. – fkn May 16 '15 at 18:35
  • I wonder why this question was upvoted so often, considering http://stackoverflow.com/questions/25230171/unexpected-type-resulting-from-the-ternary-operator and http://stackoverflow.com/questions/8002603/why-does-the-ternary-operator-unexpectedly-cast-integers and http://stackoverflow.com/questions/14796537/ternary-operator .... – Marco13 May 17 '15 at 00:37

5 Answers5

25

The if/else and the conditional (ternary) expression are not quite equivalent. The result of the conditional expression must have a type.

You're observing the effects of numeric type promotion (or type coercion).

Here's an excerpt from the language spec (see here), from the section describing the return value of the conditional expression:

Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:

The final such case is (I've omitted the other cases here):

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.

Here's the additional spec related to binary numeric promotion:

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.

It's the first case (following cases omitted). double always wins.

So regardless of the order of the 2nd and 3rd operands in your conditional expression, the return type of the expression will be promoted to double.

Community
  • 1
  • 1
pb2q
  • 58,613
  • 19
  • 146
  • 147
  • I think you really have to look into the rules for binary numeric promotion to fully understand what is going on. That can perform an unboxing conversion to int and double and then a widening of int to double. The same as if you had said `new Integer(1) + new Double(1.0)`. – Mike Zboray May 16 '15 at 18:51
6

Put simply, the ternary operator isn't different from other operators where numeric type promotion is concerned.

When you have something like System.out.println(1 + 1.0), you expect it to print 2.0, because the operands used in the output are subject to numeric type promotions.

It's exactly the same with the ternary operator: System.out.println(true ? 1 : 1.0) will print 1.0 after doing the same numeric type promotions it would have done if the expression was 1 + 1.0.

It works that way for a very simple reason: the type of the operator's result should be known at compile-time, while its actual result is determined at run-time.

4

Short answer

The first example explicitly typed as Object, which causes an upcast.

The second example is implicitly typed as Double, which causes numeric widening.


Long answer

In the example with Object, there is no conversion of values, just an upcast, and 1 is printed.

Object result;
if (1 == 1)
    result = new Integer(1);
else
    result = new Double(1.0);

If you instead declared using Double, it would be a widening and print 1.0.

Double result;
if (1 == 1)
    result = new Integer(1);
else
    result = new Double(1.0);

These are rather straightforward since there is an explicit type.

The ternary expression, however, has no explicit type, and the rules are non-trivial.

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.

  • 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 (§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).

  • 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).

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

The "promoted type" of numerics Integer and Double is Double.

Community
  • 1
  • 1
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
2

In addition to @pb2q answer

you can verify it as

public class test {
    public static void main(String[] args) {
    Object result;
    Boolean check = true;

        if (check)
            result = new Integer(1);
        else
            result = new Double(1.0);
        System.out.println(result);
        result = check ? new Integer(2) : new Double(1.0);
        System.out.println(result);
    }
}

it will print 2.0 instead of 2 because of numeric promotion

sparsh610
  • 1,552
  • 3
  • 27
  • 66
1

In addition to the existing answers, you can avoid the problem by specific casting to the type you want:

Object result = args.length < 100 ? (Object)2 : (Object)1.0;

Casting to Object boxes the integer as an Integer, and the double as a Double. The expressions on either side of the ":" are then both of type Object, so the compiler does not need to generate any further conversions.

Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75