Consider the following snippet:
int i = 99999999;
byte b = 99;
short s = 9999;
Integer ii = Integer.valueOf(9); // should be within cache
System.out.println(new Integer(i) == i); // "true"
System.out.println(new Integer(b) == b); // "true"
System.out.println(new Integer(s) == s); // "true"
System.out.println(new Integer(ii) == ii); // "false"
It's obvious why the last line will ALWAYS prints "false": we're using == reference identity comparison, and a new object will NEVER be == to an already existing object.
The question is about the first 3 lines: are those comparisons guaranteed to be on the primitive int, with the Integer auto-unboxed? Are there cases where the primitive would be auto-boxed instead, and reference identity comparisons are performed? (which would all then be false!)
Yes. JLS §5.6.2 specifies the rules for binary numeric promotion. In part:
When an operator applies binary
numeric promotion to a pair of
operands, each of which must denote a
value that is convertible to a numeric
type, the following rules apply, in
order, using widening conversion
(§5.1.2) to convert operands as
necessary:
If any of the operands is of a
reference type, unboxing conversion
(§5.1.8) is performed.
Binary numeric promotion applies for several numeric operators, including "the numerical equality operators == and !=."
JLS §15.21.1 (Numerical Equality Operators == and !=) specifies:
If the operands of an equality
operator are both of numeric type, or
one is of numeric type and the other
is convertible (§5.1.8) to numeric
type, binary numeric promotion is
performed on the operands (§5.6.2).
In contrast, JLS §15.21.3 (Reference Equality Operators == and !=) provides:
If the operands of an equality
operator are both of either reference
type or the null type, then the
operation is object equality
This fits the common understanding of boxing and unboxing, that's it only done when there's a mismatch.
I will first explain precisely when == is a reference equality, and precisely when it's a numerical equality. The conditions for reference equality is simpler, so it will be explained first.
JLS 15.21.3 Reference Equality Operators == and !=
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
This explains the following:
System.out.println(new Integer(0) == new Integer(0)); // "false"
Both operands are Integer, which are reference types, and that's why the == is reference equality comparison, and two new objects will never be == to each other, so that's why it prints false.
For == to be numerical equality, at least one of the operand must be a numeric type; this is specified as follows:
JLS 15.21.1 Numerical Equality Operators == and !=
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible to numeric type, binary numeric promotion is performed on the operands. If the promoted type of the operands is int or long, then an integer equality test is performed; if the promoted type is float or double`, then a floating-point equality test is performed.
Note that binary numeric promotion performs value set conversion and unboxing conversion.
Thus, consider the following:
System.out.println(new Integer(0) == 0); // "true"
This prints true, because:
the right operand is a numeric int type
the left operand is convertible to a numeric type, by unboxing to int
therefore == is a numerical equality operation
Summary
If both operands of == and != are reference types, it will always be a reference equality operation
It doesn't matter if the operands are convertible to numeric types
If at least one of the operand is a numeric type, it will always be a numerical equality operation
Auto-unboxing on one (at most!) of the operands will be performed if necessary
References
JLS 4.2. Primitive Types and Values
"The numeric types are the integral types and the floating-point types."
Java Language Guide/Autoboxing
JLS 5.1.8 Unboxing Conversion
JLS 15.21.1 Numerical Equality Operators == and !=
JLS 15.21.3 Reference Equality Operators == and !=
JLS 5.6.2 Binary Numeric Promotion
Related questions
When comparing two Integers in Java does auto-unboxing occur?
Why are these == but not equals()?
Java: What’s the difference between autoboxing and casting?
Related
This question already has answers here:
When using == for a primitive and a boxed value, is autoboxing done, or is unboxing done
(3 answers)
Closed 7 years ago.
For example:
Long objectLong = 555l;
long primitiveLong = 555l;
System.out.println(objectLong == primitiveLong); // result is true.
Is there invocation objectLong.longValue() method to compare Long to long or maybe some other way?
As ever, the Java Language Specification is the appropriate resource to consult
From JLS 15.21.1 ("Numerical Equality Operators == and !="):
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
Then from 5.6.2 (binary numeric promotion):
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
[...]
So the Long is unboxed to a long. Your code is equivalent to:
Long objectLong = 555l;
long primitiveLong = 555l;
// This unboxing is compiler-generated due to numeric promotion
long tmpLong = objectLong.longValue();
System.out.println(tmpLong == primitiveLong);
This question already has answers here:
Unexpected type resulting from the ternary operator
(4 answers)
Closed 8 years ago.
Object myObject = true ? new Integer(25) : new Double(25.0);
System.out.println(myObject);
Strangely, it outputs 25.0 instead of 25
Whats going on?
Your code returns the second operand (new Integer(25)) as you expected, but it converts it to a Double due to the following rules.
Here's what JLS 15.25 says :
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).
And the numeric promotion :
5.6.2. Binary Numeric Promotion
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
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.
In your example, you have an Integer and a Double. They are unboxed to int and double and then the int is converted to a double.
There is some wierd autoboxing stuff going on - you can see it better if you use different numbers:
Object myObject = true ? new Integer(25) : new Double(22.0);
Now, myObject will still be assigned a Double(25.0), not the 22.0 you would expect if the conditional didn't work. Basically, because Java thinks you are doing some sort of calculation involving an int and a double it returns the result of the iif as a "double" primative and then autoboxes it back to a Double().
You could also get it to behave as expected by forcing it to treat the values as type Object():
Object myObject = true ? (Object) new Integer(25) : (Object) new Double(22.0);
After compilation
Object myObject = true ? new Integer(25) : new Double(25.0);
will be something as below
Object myObject = (double) new Integer(25);
So, it returns new Integer(25) as expected but it gets converted to double.
As Eran mentioned, as per JLS 5.6.2:
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.
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
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.
Otherwise, if either operand is of type float, the other is converted
to float.
Otherwise, if either operand is of type long, the other is converted
to long.
Otherwise, both operands are converted to type int.
I have two variables that should be compared:
Double a = 1D;
Double b = 2D;
if (a > b) {
System.out.print("Ok");
}
In this case java will use autoboxing or compare two object's references?
From section 15.20.1 of the JLS:
The type of each of the operands of a numerical comparison operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs. Binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then signed integer comparison is performed; if this promoted type is float or double, then floating-point comparison is performed.
Section 5.6.2 starts with:
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order, using widening conversion (§5.1.2) to convert operands as necessary:
If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed.
So yes, unboxing is performed. > has no meaning for references themselves.
More interesting is the == case where both options would be possible - and in that case, if either operand is a primitive and the other can be converted via numeric promotion, then that happens... but if both are reference types, the reference comparison is performed. For example:
Double d1 = new Double(1.0);
Double d2 = new Double(1.0);
System.out.println(d1 == d2); // Prints false due to reference comparison
It will use autoboxing. You cannot do greater than on references
This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
Booleans, conditional operators and autoboxing
Java, Google Collections Library; problem with AbstractIterator?
The code below produces a NPE:
Integer test = null;
Integer test2 = true ? test : 0;
System.out.println(test2);
To correctly print out "null" without an exception requires this code:
Integer test = null;
Integer test2 = true ? test : (Integer)0;
System.out.println(test2);
It's obvious in the first example that "test" is being unboxed (converted to native int), but why? And why does changing the other expression in the ternary operator (as in the 2nd example) fix it? Can anyone provide some kind of narrative of exactly when, what, and why stuff in both of the examples gets boxed and unboxed?
From section 15.25 of the Java Language Specification:
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 type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
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 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 Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
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 unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
So it's following the final bullet, performing binary numeric promotion, which performs an unboxing conversion. So the type of the conditional operator expression is int, even though you're assigning it to an Integer. It's trying to perform the unboxing conversion on null, hence the exception.
Run the following Java code:
boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;
Why is there a NullPointerException?
The return type of the conditional expression b ? d1.doubleValue : d2 is double. A conditional expression must have a single return type. Following the rules for binary numeric promotion, d2 is autounboxed to a double, which causes a NullPointerException when d2 == null.
From the language spec, section §15.25:
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. Note that binary numeric
promotion performs unboxing conversion
(§5.1.8) and value set conversion
(§5.1.13).
Because the two expressions around : must return the same type. This means Java tries to convert the expression d2 to double. This means the bytecode calls doubleValue() on d2 -> NPE.
You should generally avoid mixed type computation; compounding this with ?: conditional/ternary only makes it worse.
Here's a quote from Java Puzzlers, Puzzle 8: Dos Equis:
Mixed-type computation can be confusing. Nowhere is this more apparent than conditional expression. [...]
The rules for determining the result type of a conditional expression are too long and complex to reproduce in their entirety, but here are three key points.
If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representible in type T, the type of the conditional expression is T.
Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
Point 3 is applied here, and it resulted in unboxing. When you unbox null, naturally a NullPointerException is thrown.
Here's another example of mixed-type computation and ?: that may be surprising:
Number n = true ? Integer.valueOf(1) : Double.valueOf(2);
System.out.println(n); // "1.0"
System.out.println(n instanceof Integer); // "false"
System.out.println(n instanceof Double); // "true"
Mixed-type computation is the subject of at least 3 Java Puzzlers.
In closing, here's what Java Puzzlers prescribes:
4.1. Mixed-type computations are confusing
Prescription: Avoid mixed-type computations.
When using the ?: operator with numeric operands, use the same numeric type for both the second and third operands.
On prefering primitive types to boxed primitives
Here's a quote from Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives:
In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.
There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.
Related questions
What is the difference between an int and an Integer in Java/C#?
Why does autoboxing in Java allow me to have 3 possible values for a boolean?
Is it guaranteed that new Integer(i) == i in Java? (YES!!!)
When comparing two Integers in Java does auto-unboxing occur? (NO!!!)
Java noob: generics over objects only? (yes, unfortunately)
Why does int num = Integer.getInteger(“123”) throw NullPointerException?
Return same type for both condition like below and you will get result.
boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1 : (Double)d2;
System.out.println(d);