Unwanted autoboxing magic on Numbers
Asked Answered
F

2

9

The following program prints respectively 'false' and 'true':

Number n = true ? new Long(1) : new Double(2.0);
System.out.println(n instanceof Long);
System.out.println(n instanceof Double);

So it will not be a Long but a Double. However, it works as intended on normal classes: Having

class B {}
class D1 extends B {}
class D2 extends B {}

this will print 'true':

B b = true ? new D1() : new D2();
System.out.println(b instanceof D1);

meaning that it is not working the same like the above example.

I'm sure that there is something related to autoboxing, but is it really the way it should work? Why she uses boxing, when the Number class is a superclass of both Long and Double, so that expression could be evaluated to Number?

It's really a pain, because when printing the n, it prints as a double value. (I know it is easy to workaround, but drove me crazy)

Forage answered 8/10, 2012 at 20:20 Comment(4)
The ?: takes the type of the last expression. If you made it null it would be a Long :P.Durante
No, it does not takes the type of the last expression, just look at the second example. Of course the code is just an example, but imagine that there is something real boolean information where now the 'true' stands in the trenary.Forage
Why the close votes? This looks like a damn good question to me.Firing
Nice question. But looks like its already answered here: #8099453Beauregard
B
8

Let's take out the language lawyer's book here: JLS §15.25

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.

Long and Double are not the same type - does not apply.

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

Neither value is primitive - does not apply.

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

Neither value is null - does not apply.

  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
    • [... special cases for byte/short/char and their boxed equivalents...]
    • 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.

This rule does apply here, which means that the result type of the conditional operator is as if both values were unboxed. Is suppose the reasoning behind that was that otherwise Number n = bool ? 1 : 2.0 and Number n = bool ? new Long(1) : new Double(2.0) have different values. This behaviour would also be unexpected and - worse - inconsistent.

Betrothed answered 8/10, 2012 at 20:46 Comment(0)
D
2

Its simply Look to the byte code and you will see (simply modified your example)

Number n = true ? new Long(166666) : new Double(24444.0);
System.out.println(Boolean.toString(n instanceof Long));
System.out.println(Boolean.toString(n instanceof Double));

Byte Code

_new 'java/lang/Long'

dup
ldc 166666
invokespecial 'java/lang/Long.<init>','(J)V'
invokevirtual 'java/lang/Long.longValue','()J'
l2d
invokestatic 'java/lang/Double.valueOf','(D)Ljava/lang/Double;'
astore 1

Main point is l2d it makes next steps

Pops a long integer off of the stack, casts it into a double precision floating point number, and pushes the double back onto the stack. Notice that this can cause loss of precision (the significand in a double is 54 bits, compared to 64 bits for the long) though not loss of magnitude (since the range of a double is greater than the range of a long). Rounding is done using the IEEE 754 round-to-nearest mode.

And after this all is good, so you will have Double instance but with Long Value! If you look in Debug mode you will see that our number is Double but value from Long, It describes above in byte code

We can see it in byte code

getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
aload 1
_instanceof 'java/lang/Long'
invokestatic 'java/lang/Boolean.toString','(Z)Ljava/lang/String;'
invokevirtual 'java/io/PrintStream.println','(Ljava/lang/String;)V'
getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
aload 1
_instanceof 'java/lang/Double'
invokestatic 'java/lang/Boolean.toString','(Z)Ljava/lang/String;'
invokevirtual 'java/io/PrintStream.println','(Ljava/lang/String;)V'
return
Dace answered 8/10, 2012 at 20:50 Comment(1)
Sweet, it's good to know (even if this answers the how, not the why).Forage

© 2022 - 2024 — McMap. All rights reserved.