skip to content

Type Conversion & Casting

How values move between types: widening and narrowing for primitives, up- and downcasting for references, autoboxing, wrapper caching and numeric promotion. This area produces a disproportionate share of Java trick questions.

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

explore

questions

23

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

What is binary numeric promotion in Java, and what type results when you add a byte and a short?

level: juniorimportance: must knowfreq 55%

basics

~10 s

When you do arithmetic on two numbers, Java converts both to a common type before computing. Anything smaller than int (byte, short, char) becomes int first. So byte + short gives an int.

open as a page

What is a widening primitive conversion in Java, and why does it happen automatically?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Widening turns a smaller numeric type into a bigger one, like int to long or int to double. Java does it automatically because the bigger type can always hold the value, so no data is lost.

open as a page

What is the difference between widening and narrowing reference conversion in Java (upcasting vs downcasting)?

level: juniorimportance: must knowfreq 78%

basics

~10 s

Upcasting treats an object as one of its parent types and happens automatically. Downcasting treats it as a more specific child type and must be written explicitly with a cast like (Dog) animal.

open as a page

What are wrapper classes in Java, and why do they exist?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Wrapper classes like Integer, Long, Double, Boolean, and Character are object versions of the eight primitive types. They exist because generics and collections only hold objects, not primitives, and they add helper methods and constants.

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

What surprising results can binary numeric promotion cause in mixed int/long/float/double arithmetic, and how do you avoid them?

level: middleimportance: must knowfreq 52%

basics

~20 s

Because both operands are promoted to a common type, an int/int division stays integer (truncates), and an int times int can overflow before being assigned to a long. Cast one operand to long or double first to fix it.

open as a page

What is a narrowing primitive conversion, why does it require an explicit cast, and what can go wrong?

level: middleimportance: must knowfreq 70%

basics

~20 s

Narrowing converts a bigger type into a smaller one, like double to int or int to byte. Java does not do it automatically because the value might not fit, so you must write a cast like (int)x, and you can lose data.

open as a page

What is a ClassCastException, when does it occur, and how do you prevent it?

level: middleimportance: must knowfreq 72%

basics

~10 s

It is a runtime error thrown when you cast an object reference to a type the object actually isn't. Prevent it by checking the type first with instanceof before casting.

open as a page

Why does comparing two Integer values with == give true for 127 but false for 128?

level: middleimportance: must knowfreq 85%

basics

~20 s

Integer.valueOf caches the small values -128..127, so autoboxing 127 twice returns the same object and == is true. 128 is outside the cache, so two new objects are created and == compares references, giving false. Always use .equals for wrappers.

open as a page

How are char values treated in arithmetic expressions, and what does ''a' + 1' evaluate to?

level: juniorimportance: should knowfreq 48%

basics

~10 s

In arithmetic, a char is treated as its numeric Unicode code value and promoted to int. 'a' has code 97, so 'a' + 1 evaluates to the int 98, not the character 'b'.

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 is unary numeric promotion, and where does it differ from binary numeric promotion?

level: middleimportance: should knowfreq 40%

basics

~20 s

Unary numeric promotion converts a single operand. If it is smaller than int (byte, short, char), it becomes int; otherwise it keeps its type. It applies to things like unary minus, the array index, bit shifts, and ~.

open as a page

Walk through exactly how Java computes (byte) 257 and (short) 70000, and explain the underlying mechanism.

level: middleimportance: should knowfreq 40%

basics

~20 s

Java keeps only the low bits that fit the smaller type and throws the rest away. (byte) 257 is 1 because 257 is 256+1 and only the low 8 bits survive. (short) 70000 is 4464 for the same reason with 16 bits.

open as a page

What is the difference between Integer.parseInt and Integer.valueOf, and how do radix overloads and parsing errors work?

level: middleimportance: should knowfreq 60%

basics

~20 s

parseInt returns a primitive int; valueOf returns an Integer object (possibly from the cache). Both can take a radix (e.g. base 16) and both throw NumberFormatException on bad input. Use parseInt when you want an int, valueOf when you need the wrapper.

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

How does numeric promotion interact with method overload resolution and autoboxing when the compiler chooses which overloaded method to call?

level: seniorimportance: should knowfreq 35%

basics

~20 s

When picking an overloaded method, Java first tries widening primitives (like numeric promotion), then boxing, then varargs — in that order. So for a short argument it prefers a method taking int (widening) over one taking Integer (boxing).

open as a page

How do widening rules apply inside arithmetic expressions (numeric promotion), and what surprises does that cause?

level: seniorimportance: should knowfreq 48%

basics

~20 s

In math expressions Java automatically widens small types to at least int before computing. So byte + byte gives an int, and mixing an int with a double makes the whole thing a double. This is why byte b = b1 + b2; needs a cast.

open as a page

Why can the Java compiler accept some casts that fail at runtime, but reject others outright as compile errors?

level: seniorimportance: should knowfreq 48%

basics

~20 s

The compiler rejects a cast only when the types could never be related, so it can never succeed. If the types are on the same inheritance line, the cast is possible, so the compiler allows it and lets the JVM check at runtime.

open as a page

Why is Integer.compare(a, b) safer than writing a - b, and what is the Math.abs(Integer.MIN_VALUE) quirk?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Integer.compare(a, b) never overflows, but the old trick a - b can overflow when the difference exceeds int range and then return the wrong sign. Similarly Math.abs(Integer.MIN_VALUE) overflows and stays negative, because MIN_VALUE has no positive counterpart.

open as a page

As a library designer, how do you prevent silent precision loss and overflow from primitive conversions, and which JLS conversion contexts are involved?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Avoid letting numbers silently shrink or lose digits. Use long instead of int where overflow is possible, BigDecimal for exact money math, and helpers like Math.toIntExact that throw on overflow. Pick API parameter types that don't force lossy conversions on callers.

open as a page

How do generics, type erasure, and reference conversion interact to produce delayed ClassCastExceptions (heap pollution)?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Generic type info is erased at runtime, so unsafe code can put the wrong type into a collection. The error then appears later, at the hidden cast the compiler adds when you read the element, not where the bad value went in.

open as a page

As an architect, how do you reason about autoboxing performance and the integer-cache range across a large, multi-environment Java system?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Avoid boxing in hot paths by using primitives, primitive streams, and primitive-specialized collections; never rely on the integer cache (==) for correctness because its range can differ by JVM config. Enforce these with static analysis and benchmarks rather than intuition.

open as a page