Why do we use autoboxing and unboxing in Java? - Stack Overflow
autoboxing - How does auto boxing/unboxing work in Java? - Stack Overflow
Friendly reminder about Integer, int, nulls and autoboxing/unboxing
I ignore autoboxing warnings, until the other day..
ResultSet RS = doSomeDatabaseQuery();
Double foo = RS.getDouble("someDoubleField");Silly me, thinking "getDouble()", which returns a field from a database which can quite plausibly be NULL, would return a Double. No, it returns a double. If the value in the db was NULL, it returns a 0.
What you actually have to do is:
ResultSet RS = doSomeDatabaseQuery();
double foo = RS.getDouble("someDoubleField");
Double bar = RS.wasNull() ? null : Double.valueOf(foo);Because I had autoboxing warnings turned off, we didn't notice this bug. Let this be a lesson! Don't ignore warnings!
More on reddit.comJava autoboxing and the ternary operator
Videos
I don't fully understand what Autoboxing is
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.
Auto Boxing is used to convert primitive data types to their wrapper class objects. Wrapper class provide a wide range of function to be performed on the primitive types. The most common example is :
int a = 56;
Integer i = a; // Auto Boxing
It is needed because of programmers easy to be able to directly write code and JVM will take care of the Boxing and Unboxing.
Auto Boxing also comes in handy when we are working with java.util.Collection types. When we want to create a Collection of primitive types we cannot directly create a Collection of a primitive type , we can create Collection only of Objects. For Example :
ArrayList<int> al = new ArrayList<int>(); // not supported
ArrayList<Integer> al = new ArrayList<Integer>(); // supported
al.add(45); //auto Boxing
Wrapper Classes
Each of Java's 8 primitive type (byte,short,int,float,char,double,boolean,long) hava a seperate Wrapper class Associated with them. These Wrapper class have predefined methods for preforming useful operations on primitive data types.
Use of Wrapper Classes
String s = "45";
int a = Integer.parseInt(s); // sets the value of a to 45.
There are many useful functions that Wrapper classes provide. Check out the java docs here
Unboxing is opposite of Auto Boxing where we convert the wrapper class object back to its primitive type. This is done automatically by JVM so that we can use a the wrapper classes for certain operation and then convert them back to primitive types as primitives result int faster processing. For Example :
Integer s = 45;
int a = s; auto UnBoxing;
In case of Collections which work with objects only auto unboxing is used. Here's how :
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(45);
int a = al.get(0); // returns the object of Integer . Automatically Unboxed .
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.
This confusion can be cleared by using a switch of javac -XD-printflat which is very helpful in cases such as this one. So to unravel the mystery of boxing and unboxing you can write a simple program like following :
import java.util.*;
public class Boxing{
public static void main(String[] args){
Double d1 = 10.123;
Float f1 = 12.12f;
Long l1 = 1234L;
Integer i1 = 55555;
Short s1 = 2345;
Byte b1 = 89;
double d2 = d1;
float f2 = f1;
long l2 = l1;
int i2 = i1;
short s2 = s1;
byte b2 = b1;
}
}
and now we compile the above file as:
javac -XD-printflat -d src/ Boxing.java
output of this command is a java file with all the syntactic sugar (Generic types, enhanced for loop and in this case boxing-unboxing etc) removed. following is the output
import java.util.*;
public class Boxing {
public Boxing() {
super();
}
public static void main(String[] args) {
Double d1 = Double.valueOf(10.123);
Float f1 = Float.valueOf(12.12F);
Long l1 = Long.valueOf(1234L);
Integer i1 = Integer.valueOf(55555);
Short s1 = Short.valueOf(2345);
Byte b1 = Byte.valueOf(89);
double d2 = d1.doubleValue();
float f2 = f1.floatValue();
long l2 = l1.longValue();
int i2 = i1.intValue();
short s2 = s1.shortValue();
byte b2 = b1.byteValue();
}
}
this is how java does boxing unboxing. using valueOf and ***Value methods.