Today I’m going to discuss quite an interesting topic of autoboxing and unboxing in Java. Let’s remind what it is exactly.
Autoboxing – automatic conversion of primitive type to corresponding object wrapped class in Java.
Unboxing – automated conversion of wrapped class to corresponding primitive type.
Primitive Type | Wrapper Class |
boolean | Boolean |
byte | Byte |
char | Character |
float | Float |
int | Integer |
long | Long |
short | Short |
double | Double |
null | null |
In the example below, we can see examples of autoboxing and unboxing at the same time.
public class Test{
public static void main(String[] args){
Integer a = 12; //autoboxing
int b = a; //unboxing
}
}
Let’s compile it
javac Test.java
and disassemble to see what is under the hood there
javap -c Test.class
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 12
2: invokestatic #7 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #13 // Method java/lang/Integer.intValue:()I
10: istore_2
11: return
}
So, the compiler just transformed previous example into this form
public class Test{
public static void main(String[] args){
Integer a = Integer.valueOf(12);
int b = a.intValue();
}
}
The same rule applies for all other types
Autoboxing | Unboxing |
Boolean Boolean.valueOf(boolean val) | boolean Boolean.booleanValue() |
Byte Byte.valueOf(byte val) | byte Byte.byteValue() |
Character Character.valueOf(char val) | char Character.charValue() |
Float Float.valueOf(float val) | float Float.floatValue() |
Integer Integer.valueOf(int val) | int Integer.intValue() |
Long Long.valueOf(long val) | long Long.longValue() |
Short Short.valueOf(short val) | short Short.shortValue() |
Double Double.valueOf(double val) | double Double.doubleValue() |
Pretty easy – you can say and would be right, but let me ask you some questions.
Q: In the code below for the section a == b will it Autoboxing or Unboxing?
public class Test{
public static void main(String[] args){
Boolean a = true;
boolean b = false;
boolean c = a == b;
}
}
A: Unboxing is the right answer.
Disassembled code as proof below
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: invokestatic #7 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
4: astore_1
5: iconst_0
6: istore_2
7: iload_2
8: aload_1
9: invokevirtual #13 // Method java/lang/Boolean.booleanValue:()Z
12: if_icmpne 19
15: iconst_1
16: goto 20
19: iconst_0
20: istore_3
21: return
}
Now, when we covered previous example this question should be easy for you.
Q: What value will be assigned to variable b in the code below?
public class Test{
public static void main(String[] args){
Boolean a = null;
boolean b = a;
}
}
A: no value will be assigned to b due to exception during execution.
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:4)
Code in the question is equal to this one
public class Test{
public static void main(String[] args){
Boolean a = null;
boolean b = a.booleanValue(); //null.booleanValue()
}
}
Q: What value will be assigned to variable c in the example below?
public class Test{
public static void main(String[] args){
Long a = 20L;
long b = 20;
boolean c = a.equals(b);
}
}
A: the correct answer – true.
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc2_w #7 // long 20l
3: invokestatic #9 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
6: astore_1
7: ldc2_w #7 // long 20l
10: lstore_2
11: aload_1
12: lload_2
13: invokestatic #9 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
16: invokevirtual #15 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
19: istore 4
21: return
}
which is the same as
public class Test{
public static void main(String[] args){
Long a = 20L;
long b = 20;
boolean c = a.equals(Long.valueOf(b));
}
}
Q: What value will be assigned to variable c in the example below?
public class Test{
public static void main(String[] args){
Long a = 20L;
boolean c = a.equals(20);
}
}
A: the correct answer – false.
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc2_w #7 // long 20l
3: invokestatic #9 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
6: astore_1
7: aload_1
8: bipush 20
10: invokestatic #15 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #20 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
16: istore_2
17: return
}
In this example there is no type reference so 20 is represented as most appropriate integer type.
public class Test{
public static void main(String[] args){
Long a = 20L;
boolean c = a.equals(Integer.valueOf(20));
}
}
So, even if autoboxing/unboxing logic seems pretty straightforward and easy you still need to keep attention to details. As best practice to avoid memory and/or performance-related issues you shouldn’t rely on the autoboxing/unboxing mechanism in your code. The most appropriate case when this mechanism is really needed is when you store primitive types in the collection.
Photo by Ante Hamersmit on Unsplash