When using == for a primitive and a boxed value, is autoboxing done, or is unboxing done
It is defined in the JLS #15.21.1:
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).
And JLS #5.6.2:
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
[...]
So to answer your question, the Integer
is unboxed into an int
.
In Java, does == Box or Unbox when comparing an object and a constant value?
What you call "a constant value" is an int
literal, so its type is int
.
JLS 15.21.1 says:
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.
In your v1 == 1000
test, 1000
is of numeric type and v1
is convertible to numeric type, so binary numeric promotion is performed.
JLS 5.6.2 (Binary numeric promotion) says:
If any operand is of a reference type, it is subjected to unboxing conversion
Hence, the Integer
operand - v1
- is unboxed to an int
and a comparison of two int
s is performed. Therefore the result of the comparison is true
.
When you compare two reference types - v1 == v2
- no unboxing takes places, only the references are compared, as written in JLS 15.21.3:
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
Since 1000
is too large to be cached by the Integer
cache, b1
and b2
are not referencing the same instance, and therefore the result of the comparison is false
.
Java autoboxing and comparison of Objects using operators
Because <
, >
, >=
, and <=
are numerical comparison, and thus, the compiler knows it has to do unboxing.
However, ==
and !=
always work as reference comparators for non-primitive types.
How does auto boxing/unboxing work in Java?
When in doubt, check the bytecode:
Integer n = 42;
becomes:
0: bipush 42
2: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
So in actuality, valueOf()
is used as opposed to the constructor (and the same goes for the other wrapper classes). This is beneficial since it allows for caching, and doesn't force the creation of a new object on each boxing operation.
The reverse is the following:
int n = Integer.valueOf(42);
which becomes:
0: bipush 42
2: invokestatic #16 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: invokevirtual #22 // Method java/lang/Integer.intValue:()I
8: istore_1
i.e. intValue()
is used (again, it's analogous for the other wrapper types as well). This is really all auto(un)boxing boils down to.
You can read about boxing and unboxing conversions in JLS §5.1.7 and JLS §5.1.8, respectively.
Why do we use autoboxing and unboxing in Java?
Some context is required to fully understand the main reason behind this.
Primitives versus classes
Primitive variables in Java contain values (an integer, a double-precision floating point binary number, etc). Because these values may have different lengths, the variables containing them may also have different lengths (consider float
versus double
).
On the other hand, class variables contain references to instances. References are typically implemented as pointers (or something very similar to pointers) in many languages. These things typically have the same size, regardless of the sizes of the instances they refer to (Object
, String
, Integer
, etc).
This property of class variables makes the references they contain interchangeable (to an extent). This allows us to do what we call substitution: broadly speaking, to use an instance of a particular type as an instance of another, related type (use a String
as an Object
, for example).
Primitive variables aren't interchangeable in the same way, neither with each other, nor with Object
. The most obvious reason for this (but not the only reason) is their size difference. This makes primitive types inconvenient in this respect, but we still need them in the language (for reasons that mainly boil down to performance).
Generics and type erasure
Generic types are types with one or more type parameters (the exact number is called generic arity). For example, the generic type definition List<T>
has a type parameter T
, which can be Object
(producing a concrete type List<Object>
), String
(List<String>
), Integer
(List<Integer>
) and so on.
Generic types are a lot more complicated than non-generic ones. When they were introduced to Java (after its initial release), in order to avoid making radical changes to the JVM and possibly breaking compatibility with older binaries, the creators of Java decided to implement generic types in the least invasive way: all concrete types of List<T>
are, in fact, compiled to (the binary equivalent of) List<Object>
(for other types, the bound may be something other than Object
, but you get the point). Generic arity and type parameter information are lost in this process, which is why we call it type erasure.
Putting the two together
Now the problem is the combination of the above realities: if List<T>
becomes List<Object>
in all cases, then T
must always be a type that can be directly assigned to Object
. Anything else can't be allowed. Since, as we said before, int
, float
and double
aren't interchangeable with Object
, there can't be a List<int>
, List<float>
or List<double>
(unless a significantly more complicated implementation of generics existed in the JVM).
But Java offers types like Integer
, Float
and Double
which wrap these primitives in class instances, making them effectively substitutable as Object
, thus allowing generic types to indirectly work with the primitives as well (because you can have List<Integer>
, List<Float>
, List<Double>
and so on).
The process of creating an Integer
from an int
, a Float
from a float
and so on, is called boxing. The reverse is called unboxing. Because having to box primitives every time you want to use them as Object
is inconvenient, there are cases where the language does this automatically - that's called autoboxing.
Why wrapped type unboxed instead of boxing the primitive?
Consider this scenario:
Boolean b1 = new Boolean(false); //I know you shouldn't be doing this but it's valid
boolean foo = b1 == false;
Boolean bar = b1 == false;
Now if things worked like you probably expect them to be, foo
would be true and bar
would be false. Or, alternatively both could be false but that would mean autoboxing everything all the time if just a single boxed primitive occurs in the expression. (And then potentially unboxing it for the assignment.)
I wouldn't consider that a good trade-off. Granted, dealing with NPEs from unboxing conversions is ugly but it's something you have to do anyway in most unboxing scenarios.
Java autoboxing rules
Unboxing will be happing when arithmetic operators
, comparison operators
appear.
eg:
Integer a = 10;
a = a+10; //1.unboxing a to int 2.calculate a+10 3.boxing 20 to Integer.
System.out.print(a > 10); //1.unboxing a to int 2. compare
But when ==
appear, it depends.
If boxing type appear on both side
, it will compare the reference
.But if base type appear on one side
, and the other side is a boxing type, the boxing type will unboxing
to base type.
eg:
Integer a = new Integer(129);
Integer b = new Integer(129);
System.out.println(a == b); // compare reference return false
System.out.println(a == 129); // a will unboxing and compare 129 == 129 return true
PS: In Java.lang.Integer
Cache to support the object identity semantics of autoboxing for values between -128 and 127 (inclusive) as required by JLS.
See source code
So:
Integer a = 127;
Integer b = 127; //cached, the same as b a==b return ture
Integer c = 129;
Integer d = 129; // not cached, c==d return false
Autoboxing Unboxing Operator (!=) and (==) difference
This is because 10 is in between the range [-128, 127]. For this range ==
works fine since the JVM caches the values and the comparison will be made on the same object.
Every time an Integer
(object) is created with value in that range, the same object will be returned instead of creating the new object.
See the JLS for further information.
Why is autoboxing not allowed for primitive arrays when using Arrays.sort()?
According to the JLS, Section 5.1.7, there are only the following specified boxing conversions, and arrays aren't involved in any of them:
Boxing conversion converts expressions of primitive type to
corresponding expressions of reference type. Specifically, the
following nine conversions are called the boxing conversions:
From type boolean to type Boolean
From type byte to type Byte
From type short to type Short
From type char to type Character
From type int to type Integer
From type long to type Long
From type float to type Float
From type double to type Double
From the null type to the null type
Related Topics
Why Does Java Code with an Inner Class Generates a Third Someclass$1.Class File
Hibernate/JPA - Annotating Bean Methods VS Fields
File Upload Using Selenium Webdriver and Java Robot Class
How to Add Close Button to a Jtabbedpane Tab
How to Create a Stream of Regex Matches
Java Inheritance - Calling Superclass Method
How to Invert the Case of a String in Java
Error:Java.Lang.Nosuchmethoderror: Org.Objectweb.Asm.Classwriter.<Init>(I)V
Why Are Wait() and Notify() Declared in Java's Object Class
Java.Rmi.Connectexception: Connection Refused to Host: 127.0.1.1;
Reverse Hashmap Keys and Values in Java
How Does Double to Int Cast Work in Java
Tomcat 7 "Severe: a Child Container Failed During Start"
How to Test Abstract Class in Java with Junit
Understanding the etc/Gmt Time Zone
Modifier Static Is Only Allowed in Constant Variable Declarations