Java Autoboxing and Unboxing
Java automatically converts between primitive values such as `int` and their corresponding wrapper objects such as `Integer`. This convenience can hide object creation, trigger `NullPointerException`, and affect overload resolution.
Autoboxing converts a primitive to its wrapper type, such as `int` to `Integer`; unboxing performs the reverse conversion. These conversions happen implicitly where Java's type system permits them, so code can look like it is operating entirely on one kind of value when both primitives and objects are involved.
For example, assigning an `int` to an `Integer` causes boxing, while assigning an `Integer` to an `int` causes unboxing. Java also applies these conversions when interacting with generic collections, because types such as `List<Integer>` cannot use primitive type arguments like `int`.
Unboxing requires a non-null wrapper. If an `Integer` reference contains `null` and Java needs an `int`, the implicit conversion attempts to obtain a primitive value and results in `NullPointerException`.
Boxing can also have performance consequences in frequently executed code because wrapper objects are objects and may require allocation. In addition, mixing primitive and wrapper overloads can produce results that are surprising unless you understand Java's overload-selection rules.
For backend code, prefer primitives when object semantics are unnecessary, and treat wrapper values as potentially nullable. Be especially careful at boundaries involving collections, generic APIs, arithmetic expressions, and overloaded methods.
Key points
- Autoboxing converts primitives to wrapper objects; unboxing converts wrappers to primitives.
- `Integer`, `Long`, `Double`, and similar wrappers are objects and can be `null`; primitives cannot.
- Unboxing a `null` wrapper causes `NullPointerException`.
- Generic collections require wrapper types, so `List<Integer>` relies on boxing and unboxing when primitives are inserted or retrieved.
- Repeated boxing can add allocation and garbage-collection overhead in performance-sensitive code.
- Overload resolution can change when boxing, unboxing, widening, or reference conversion are possible.
Interview questions
What are autoboxing and unboxing in Java?
Autoboxing is Java's implicit conversion from a primitive to its corresponding wrapper object, such as `int` to `Integer`. Unboxing converts the wrapper back to a primitive. The compiler inserts these conversions where the language permits them. They make APIs such as generic collections easier to use with primitive values.
What happens if you unbox a `null` wrapper?
Unboxing a `null` wrapper throws `NullPointerException`. For example, if `Integer value = null`, assigning `value` to an `int` requires an implicit unboxing operation. Because there is no primitive value inside the null reference, the operation fails. Backend code should therefore validate nullable wrapper values before arithmetic or other primitive-dependent operations.
Why do Java collections use `Integer` instead of `int`?
Java generics operate on reference types, so a type argument cannot be a primitive such as `int`. Therefore a collection of integer values is declared as `List<Integer>`. Adding an `int` to that collection triggers boxing, and retrieving an `Integer` into an `int` triggers unboxing. This behavior is convenient but can introduce object-related overhead.
Why can `Integer a = 1000; Integer b = 1000; a == b` be false?
`==` compares references when both operands are wrapper objects, rather than comparing their numeric values. Java specifies caching behavior for certain boxed primitive values, so some equal wrapper values may refer to the same object while others need not. Consequently, identity comparisons should not be used to test wrapper value equality. Use `.equals()` or unbox deliberately when value comparison is intended.
How can boxing affect backend performance?
Boxing represents a primitive as an object, which can create additional object-management work. In a hot loop or large numerical workload, repeated boxing may increase allocation and garbage-collection pressure. The impact depends on the code and runtime, so it should be measured rather than assumed. Primitive-specialized APIs or data structures can sometimes avoid this overhead.