Autoboxing & Unboxing
Overview
Before Java 5, converting between primitives and their Wrapper classes required verbose, manual code: Integer boxed = Integer.valueOf(5); and int unboxed = boxed.intValue();. This was tedious boilerplate that cluttered every codebase that used collections.
Autoboxing is the Java compiler automatically converting a primitive into its corresponding Wrapper Class when an Object is required. Unboxing is the reverse — the compiler automatically extracting the primitive value from a Wrapper Class when a primitive is required. This all happens invisibly at compile time; the compiler inserts the conversion calls for you.
While convenient, Autoboxing/Unboxing are not free. Each conversion creates a new Wrapper object (or calls a method to extract a value), which has heap allocation costs and garbage collection implications. Understanding when implicit conversions occur is critical for writing high-performance Java.
Syntax
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// --- AUTOBOXING (primitive → Object) ---
// Without Autoboxing (Old Java way):
// Integer a = Integer.valueOf(42);
// With Autoboxing (Modern Java — compiler does it for you):
Integer a = 42; // int 42 is automatically boxed to Integer
// Autoboxing in Collections (Most common use!)
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // int 10 is autoboxed to Integer(10) automatically
numbers.add(20);
// --- UNBOXING (Object → primitive) ---
// Compiler inserts .intValue() call automatically:
int sum = numbers.get(0) + numbers.get(1); // Unboxed to int for math
System.out.println("Sum: " + sum); // 30
// Unboxing in arithmetic
Integer x = 100;
Integer y = 200;
int result = x + y; // Both are unboxed to int, added, stays as int
}
}Common Pitfalls
- Triggering NullPointerException via unboxing. If you have
Integer value = null;and then doint x = value;, the compiler inserts a.intValue()call, which is called on null, causing a NullPointerException. This is an extremely sneaky bug because there is no visible method call in your source code. - Performance degradation in inner loops. If your loop runs 100 million times and the loop variable is
Integerinstead ofint, the JVM does 100 million autoboxing/unboxing operations. Each creates a heap allocation (or hits the cache for values -128 to 127). Always use primitives in performance-critical loops. - Ambiguous method resolution. If a method is overloaded with both
process(int x)andprocess(Integer x), Java's autoboxing rules can cause surprising behavior when deciding which overload to call.
Interview Questions
Integer i = 5;?The compiler transforms it into Integer i = Integer.valueOf(5);. For values in the range -128 to 127, valueOf() returns a cached Integer object (no new allocation). For values outside this range, it creates a new Integer object on the heap.
NullPointerExceptions and how?Yes, and it is one of the most surprising bugs in Java. If Integer x = null; and you write int y = x;, the compiler inserts x.intValue(), which is a method call on a null object, throwing NullPointerException. The danger is that there is no visible method call in the source code, making the stack trace confusing.
Real-World Example
Stream pipelines on large datasets are a prime area where autoboxing causes hidden performance bottlenecks. IntStream avoids boxing/unboxing entirely for numeric processing, while Stream<Integer> creates Integer objects for every element.
// Slow path: Stream<Integer> boxes every int to Integer object
long countSlow = list.stream()
.filter(n -> n > 100) // n is autoboxed Integer here
.count();
// Fast path: IntStream avoids boxing entirely!
long countFast = list.stream()
.mapToInt(Integer::intValue) // Unbox once, work with raw ints
.filter(n -> n > 100) // n is now a primitive int
.count();Check Your Knowledge
Test your understanding of Autoboxing & Unboxing with these quick questions.