Why does this return false and true?

public class Test { public static final Double DEFAULT_DOUBLE = 12.0; public static final Long DEFAULT_LONG = 1L; public static Double convertToDouble(Object o) { return (o instanceof Number) ? ((Number) o).doubleValue() : DEFAULT_DOUBLE; } public static Long convertToLong(Object o) { return (o instanceof Number) ? ((Number) o).longValue() : DEFAULT_LONG; } public static void main(String[] args){ System.out.println(convertToDouble(null) == DEFAULT_DOUBLE); System.out.println(convertToLong(null) == DEFAULT_LONG); } } 
+6
source share
2 answers

EDIT

The ternary operator does some type conversions under the hood . In your case, you mix the primitives and types of wrappers, and in this case the types of wrappers are unpacked, then the result of the triple operator "re-boxed":

If one of the second and third operands has a primitive type T, and the type of the other is the result of applying the conversion of the box (section 5.1.7) to T, then the conditional expression type is T.


So your code is essentially equivalent (except for a typo where longValue should be doubleValue ):

 public static void main(String[] args){ Double d = 12.0; System.out.println(d == DEFAULT_DOUBLE); Long l = 1L; System.out.println(l == DEFAULT_LONG); } 

Long values ​​can be cached on some JVMs, and comparing == can therefore return true. If you did all the comparisons with equals , you would get true in both cases.

Note that if you use public static final Long DEFAULT_LONG = 128L; and try:

 Long l = 128L; System.out.println(l == DEFAULT_LONG); 

It will probably print false, since long values ​​are usually cached between -128 and +127.

Note. JLS requires that the char , byte and int values ​​between -127 and +128 be cached but does not say anything about long . That way, your code can actually print false twice on another JVM.

+8
source

To understand the strange behavior, you need to parse what it does:

 (o instanceof Number) ? ((Number) o).longValue() : DEFAULT_LONG 

What we have here is a method call that returns long , and an instance of long . Two different types. However, the conditional statement ? you need to create one type; either long or long . And for this, either the second operand must be placed in the box, or the third operand must be unpacked.

In this case, JLS says that the third operand should be unpacked. JLS 15.25

"If one of the second and third operands has a primitive type T, and the type of the other is the result of applying the conversion of the box (section 5.1.7) to T, then the conditional expression type is T."

And that means your constants are unpacked and then put back into the box.

Now primitive type boxing is done by calling the valueof() method. And these methods do slightly different things depending on the base type.

  • For a floating-point valueof always creates a new object.
  • For an integral type, the valueof method sometimes creates a new object and sometimes returns an existing object.

And the last is what happens here. Long.valueof(1L) always returns the same value, and comparing with == gives true .


There are two “fixes” ... if you want to fix this:

  • Replace the ternary operator with if / else with a return in both branches.
  • Insert the second operand so that it is in the box:

     return (o instanceof Number) ? (Long) ((Number) o).longValue() : DEFAULT_LONG; 
+4
source

All Articles