10

Please consider we have code below:

Object obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue = " + obj);

And its result is:

class java.lang.Long
value = 0

Instead of:

class java.lang.Integer
value = 0

Could someone clarify why we have such functionality in Java? It is very strange for me. Do you have any example where this can be useful?

UPDATE: Here is piece of bytecode, where we can see, whats going on there

NEW java/lang/Integer
DUP
LDC "0"
INVOKESPECIAL java/lang/Integer.<init> (Ljava/lang/String;)V
INVOKEVIRTUAL java/lang/Integer.intValue ()I
I2L
INVOKESTATIC java/lang/Long.valueOf (J)Ljava/lang/Long;
ASTORE 1
  • to make the ternary statement valid, both sides should return a value of the same type – Andrew Tobilko Nov 28 '16 at 13:07
  • 2
    @PeterLawrey `Object o = false ? new Long(0) : new Integer(1);` will also return `class java.lang.Long` and `1`. It's not the type of the last argument, but rather the type that can be received by widening. – QBrute Nov 28 '16 at 13:26
  • 2
    I like it when badly researched questions get many upvotes. But reading the JLS is too much to ask for nowadays ... – Tom Nov 28 '16 at 13:30
  • 2
    To clarify, this is a duplicate of [Why does the ternary operator unexpectedly cast integers?](http://stackoverflow.com/questions/8002603/why-does-the-ternary-operator-unexpectedly-cast-integers), not of the other one. – Tunaki Nov 28 '16 at 13:37
  • 2
    See also http://stackoverflow.com/questions/25230171/unexpected-type-resulting-from-the-ternary-operator and slightly related http://stackoverflow.com/questions/8098953/tricky-ternary-operator-in-java-autoboxing – Tunaki Nov 28 '16 at 13:43

4 Answers4

12

What's going on here is the result of

  • Binary numeric promotion turning your Integer and Long types into long for use as the common type to apply to the conditional operator expression
  • Unboxing those wrapper objects
  • And then boxing the resulting value of the conditional expression

The conditional operator's second and third operands must end up having the same type, which is the resulting type of the expression. Integer and Long are not, of course, the same type.

However, as described in JLS§15.25, the compiler will apply binary numeric promotion when determining the possible common type to apply to the expression. That section has the handy Table 15.25-D which tells us that when the second operand is of type Integer and the third operand is of type Long, the compiler will do binary numeric promotion on Integer,Long. Binary numeric promotion on Integer,Long yields long. So the result of the conditional operator expression is long.

Since the expression's result type is long, the Integer or Long will have to be unboxed (and then cast, in the case of Integer).

Finally, you assign it to an Object, which forces boxing, and wraps the long in a Long. Thus, you end up with a Long containing the value 0, which matches your output.

So effectively, if we ignore the fact the compiler will optimize half of the following away since it's dealing with a constant expression thanks to the true in the code (I've used flag below instead), that code ends up being this:

Object obj = Long.valueOf(flag ? (long)(new Integer(0)).intValue() : (new Long(1)).longValue());
System.out.println(obj.getClass() + "\nvalue = " + obj);
  • The (long)(new Integer(0)).intValue() represents unboxing the Integer and casting it to long so it matches the expression result type.
  • The (new Long(1)).longValue() represents unboxing the Long so it matches the expresion result type.
  • And the Long.valueOf represents the boxing at the end.
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
4

This behavior is well explained in the JLS - 15.25. Conditional Operator ? ::

The conditional operator has three operand expressions. ? appears between the first and second expressions, and : appears between the second and third expressions.

[...]

The type of a conditional expression is determined as follows:

  • [...]

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

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Maroun
  • 94,125
  • 30
  • 188
  • 241
0

Actually long can store the value of an integer but integer can not store the value of long(Concept is widening) and you are storing it in Object that's why it's storing it in Long. internally it's also using boxing and unboxing

Possible compiler code:

Long obj = true ? new Integer(0) : new Long(1);

System.out.println(obj.getClass() + "\nvalue = " + obj);

Not working code(unable to compile):

 Integer obj = true ? new Integer(0) : new Long(1);

System.out.println(obj.getClass() + "\nvalue = " + obj);

check by yourself and let me know if you havr any doubt in it.

mayank agrawal
  • 628
  • 5
  • 20
0

This is related to how the ternary operator works.

The operands on both sides of the : must be of compatible types, so you can't have this:

true ? 1 : "Hello"

Since int and String cannot be implictly converted to each other.

In your case however, the types are compatible! An int can be implicitly converted to long. And that's what the compiler did! It saw an int and a long and decides that the expression should evaluate to a long. It unboxes the two values and implictly convert the int to a long. And finally, it boxes the resulting long so it becomes Long and put it in the variable.

Sweeper
  • 213,210
  • 22
  • 193
  • 313