skip to content

Autoboxing & Unboxing

The compiler quietly converts between primitives and wrappers, which is convenient until a null Integer is unboxed and throws NullPointerException. The other half is cost: boxing in a loop allocates, which is why primitive streams and arrays exist.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

4

What are autoboxing and unboxing in Java, and when does the compiler insert them?

level: juniorimportance: must knowfreq 78%

answer

  1. 8 primitives, 8 wrapper classes
  2. Box = valueOf, unbox = intValue
  3. Compiler-inserted, pure syntactic sugar (Java 5)
  4. Box into object context; unbox into primitive context
  5. Sugar hides NPE + allocation cost

basics

~20 s

Autoboxing is the automatic conversion of a primitive (like int) to its wrapper object (Integer). Unboxing is the reverse. The compiler inserts these conversions for you when a primitive is used where an object is expected, or vice versa.

solid answer

~40 s

Java has eight primitive types (int, long, double, boolean, etc.) and a wrapper class for each (Integer, Long, Double, Boolean...). Autoboxing is the compiler automatically wrapping a primitive into its wrapper object; unboxing is automatically extracting the primitive back out. Both were added in Java 5 to remove manual conversion noise. The compiler inserts a box (Integer.valueOf(x)) when a primitive flows into an Object/wrapper context (assignment, generics, collections, varargs of Object) and an unbox (intValue()) when a wrapper flows into a primitive context (arithmetic, comparisons against primitives, control conditions). So `Integer i = 5;` boxes and `int x = i;` unboxes. It is purely syntactic sugar: the bytecode still calls valueOf/xxxValue. Knowing exactly where it fires matters because hidden conversions create the classic NullPointerException-on-unbox and performance pitfalls.

go deeper

for a junior

Can define both directions, name a few primitive/wrapper pairs, and write Integer i = 5; int x = i;.

for a middle

Can state that it is compiler sugar (valueOf/intValue) and list the main contexts that trigger boxing vs unboxing, including generics and collections.

for a senior

Explains the bytecode, distinguishes when == does/doesn't unbox, and connects the sugar to the NPE and allocation pitfalls without prompting.

for a principal

Frames it as an API/ergonomics-vs-performance tradeoff, can reason about valueOf caching, and would set team guidance (prefer primitives/primitive streams in hot paths).

## What problem this solves Java draws a hard line between two kinds of values: - **Primitives** — `int`, `long`, `short`, `byte`, `char`, `boolean`, `float`, `double`. These are raw values stored directly (e.g. an `int` is just 32 bits). They are not objects, have no methods, and cannot be `null`. - **Wrapper (reference) types** — one class per primitive: `Integer`, `Long`, `Short`, `Byte`, `Character`, `Boolean`, `Float`, `Double`. These are real objects living on the heap, holding a single primitive inside. They have methods, can be stored in collections/generics, and **can be `null`**. Before Java 5 you converted by hand: `Integer obj = Integer.valueOf(5);` and `int x = obj.intValue();`. This was verbose, especially with collections. ## The two operations - **Autoboxing**: the compiler automatically turns a primitive into its wrapper. `Integer i = 5;` is compiled to `Integer i = Integer.valueOf(5);`. - **Unboxing**: the compiler automatically turns a wrapper back into its primitive. `int x = i;` is compiled to `int x = i.intValue();`. The word *auto* matters: you never write `valueOf`/`intValue`; the **compiler inserts the call** based on the surrounding types. The resulting bytecode is identical to writing it by hand — this is *syntactic sugar*, a source-level convenience that disappears after compilation. ## Exactly when the compiler boxes A primitive is boxed whenever it is used where a *reference type* is expected: 1. **Assignment / initialization** to a wrapper variable: `Integer i = 5;` 2. **Generics**, which can only hold objects: `List<Integer> list = new ArrayList<>(); list.add(3);` — the `3` is boxed. 3. **Method argument** typed as the wrapper or `Object`: `void f(Object o)` called as `f(7)`. 4. **Return value** when the method's return type is a wrapper. 5. **The ternary operator and varargs** under certain mixed-type rules. ## Exactly when the compiler unboxes A wrapper is unboxed whenever its underlying primitive is needed: 1. **Assignment** to a primitive variable: `int x = someInteger;` 2. **Arithmetic / bitwise operators**: `someInteger + 1`, `a * b` where `a`,`b` are `Integer` — both are unboxed first. 3. **Comparison with a relational operator** (`<`, `>`, `<=`, `>=`): `if (someInteger > 3)` unboxes. 4. **Conditions**: a `Boolean` used in `if`, `while`, `?:` is unboxed to `boolean`. 5. **Method argument** typed as the primitive. > Note: `==` between two wrappers does **not** unbox — it compares object references. `==` only unboxes when one side is a primitive. This is a frequent trap (covered in a separate question). ## Why it matters (preview of the pitfalls) Because the conversions are invisible, two whole classes of bugs hide in plain sight: - **NullPointerException on unbox**: if a wrapper is `null` and the compiler inserts `.intValue()`, you get an NPE at a line that looks like simple arithmetic or assignment. - **Performance cost**: each box allocates (or reuses a cached) object, and each unbox is a method call. In tight loops or large sums this creates garbage and slows things down. These two consequences are the practical reason interviewers ask about a feature that otherwise *just works*.

  • Does `==` between two Integer objects unbox them?
    No. `==` between two reference types compares references, not values. Unboxing only happens with `==` when exactly one operand is a primitive; then the wrapper is unboxed and a value comparison is done.
  • What bytecode does autoboxing compile to?
    A static call to the wrapper's valueOf, e.g. `Integer.valueOf(int)`; unboxing compiles to an instance call like `Integer.intValue()`.

saying these in an interview costs you the question

  • Saying autoboxing is a runtime/JVM feature rather than compiler-inserted sugar
  • Claiming there is no performance difference between primitives and wrappers
  • Thinking == between two Integers unboxes and compares values
  • Confusing direction: calling primitive-to-wrapper 'unboxing'

context

open as a page

How can autoboxing cause a NullPointerException, and how do you guard against it?

level: middleimportance: must knowfreq 74%

basics

~20 s

A wrapper like Integer can be null. When you use it where a primitive is needed, the compiler inserts an unboxing call (intValue()) on that null, which throws a NullPointerException. Check for null, or use a primitive default.

open as a page

Why does `==` between two Integer objects sometimes return true and sometimes false?

level: middleimportance: should knowfreq 68%

basics

~20 s

== on two Integer objects compares references, not values. Java caches small boxed Integers (-128 to 127), so two boxes of the same small value share one object and == is true; outside that range you get distinct objects and == is false. Use .equals() or compare as int.

open as a page

What are the performance costs of autoboxing, and how do you avoid accidental boxing in hot code?

level: seniorimportance: should knowfreq 64%

basics

~20 s

Each autobox creates (or fetches) a wrapper object and each unbox is a method call. In loops or large sums this means many heap allocations, more garbage collection, and cache-unfriendly pointer chasing. Use primitives, primitive arrays, and primitive streams (IntStream) instead of wrappers.

open as a page