Reputation: 796
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);
}
}
Upvotes: 6
Views: 398
Reputation: 719641
To understand the strange behaviour, you need to pick apart what this does:
(o instanceof Number) ? ((Number) o).longValue() : DEFAULT_LONG
What we have here is a call to method that returns a long
, and an instance of Long
. Two different types. However the conditional operator ?
needs to produce a single type; either long
or Long
. And in order to do that, either the second operand must be boxed or the third operand must be unboxed.
In this case, the JLS says that the third operand must be unboxed. JLS 15.25
"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."
And that means that your constants are being unboxed and then boxed again.
Now boxing a primitive type is done by calling the boxed type's valueof()
method. And these methods do slightly different things depending on the base type.
valueof
always creates a new objectvalueof
method sometimes creates a new object, and sometimes returns an existing object.And the latter is what is happening here. Long.valueof(1L)
is always returning the same value, and the comparison with ==
is giving true
.
There are two "fixes" ... assuming you want to fix this:
if
/ else
with a return
in both branches.Cast the second operand to force it to be boxed:
return (o instanceof Number) ?
(Long) ((Number) o).longValue() : DEFAULT_LONG;
Upvotes: 4
Reputation: 328913
EDIT
The ternary operator does some type conversions under the hood. In your case you are mixing primitives and wrapper types, in which case the wrapper types gets unboxed, then the result of the ternary operator is "re-boxed":
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.
So your code is essentially equivalent to (apart from the 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 the ==
comparison can therefore return true. If you made 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, because Long values are generally cached between -128 and +127 only.
Note: The JLS requires that char
, byte
and int
values between -127 and +128 be cached but does not say anything about long
. So your code might actually print false twice on a different JVM.
Upvotes: 8