Why can't you write List<int> in Java, and what must you use instead?
answer
- Generics take reference types only
- int -> Integer wrapper
- Autoboxing bridges the gap
- Erasure stores elements as Object
- Cost: heap objects + boxing + NPE on null
basics
~10 sGeneric type arguments must be objects, and int is a primitive, not an object. So List<int> is illegal. Use the wrapper class instead: List<Integer>. Java then converts int to Integer for you automatically.
solid answer
~40 sJava generics only accept reference types (objects) as type arguments, never primitives like int, double, or boolean. That's why List<int> won't compile. The reason is implementation: generics are erased to Object at compile time, and Object can only hold references, not raw primitive values. So you use the wrapper class, List<Integer>, and rely on autoboxing/unboxing: writing list.add(5) auto-wraps the int 5 into an Integer, and int x = list.get(0) auto-unwraps it. The trade-off is that each element is a separate heap object with pointer overhead, and boxing/unboxing in hot loops costs CPU and allocations. For performance-critical primitive collections, people reach for int[] arrays or libraries like Eclipse Collections / fastutil.
code
java · 9 lines// List<int> nums = new ArrayList<>(); // does NOT compile
List<Integer> nums = new ArrayList<>();
nums.add(5); // autobox: int -> Integer.valueOf(5)
int first = nums.get(0); // auto-unbox: Integer -> intValue()
// Watch out for null unboxing:
Integer boxed = null;
// int oops = boxed; // throws NullPointerException at runtimego deeper
Knows List<int> is illegal and that you write List<Integer> instead, relying on autoboxing.
Can explain it's because generics require reference types and that boxing has memory/CPU costs and a null-unboxing NPE risk.
Ties the restriction to type erasure (elements stored as Object), and knows the alternatives (int[], primitive streams, fastutil/Eclipse Collections).
Frames it as a language-implementation trade-off, can reason about allocation/cache impact at scale, and references Project Valhalla as the direction for primitive/value generics.
## The terms first **Primitive type:** Java has eight built-in value types — `byte`, `short`, `int`, `long`, `float`, `double`, `char`, `boolean`. A primitive variable holds the value *directly* (the bits of the number), not a reference to an object. It lives on the stack or inline in an object, and has no methods. **Reference type (object type):** Everything else — classes, interfaces, arrays. A variable of a reference type holds a *reference* (a pointer) to an object on the heap. **Wrapper class:** For each primitive there is a matching class that *wraps* a single primitive value as an object: `int`→`Integer`, `double`→`Double`, `boolean`→`Boolean`, `char`→`Character`, `long`→`Long`, etc. An `Integer` is a real object on the heap whose only job is to carry one `int`. **Generics:** A way to parameterize a type, e.g. `List<Integer>` is "a list of Integers". The thing in the angle brackets is the **type argument**. ## The rule **A generic type argument must be a reference type. Primitives are not allowed.** So: ```java List<int> nums; // COMPILE ERROR: unexpected type, required reference List<Integer> nums; // OK ``` This applies to *every* primitive and *every* generic position: `Map<int, String>`, `Optional<double>`, `Comparable<boolean>` are all illegal. ## Why — type erasure Java generics are implemented by **type erasure**: the generic type information is used by the compiler for type-checking, then *erased* in the bytecode. A `List<Integer>` and a `List<String>` are both just `List` at runtime, and internally the list stores its elements as `Object`. A primitive `int` is not an `Object` and cannot be stored in an `Object` slot — only a reference can. So the language simply forbids primitive type arguments rather than letting you write code that couldn't be represented. ## What you do instead — autoboxing You use the wrapper type and let **autoboxing/unboxing** bridge the gap: ```java List<Integer> nums = new ArrayList<>(); nums.add(5); // autobox: int 5 -> Integer.valueOf(5) int first = nums.get(0); // auto-unbox: Integer -> intValue() ``` *Autoboxing* is the compiler automatically inserting `Integer.valueOf(...)` where an `int` is used but an `Integer` is expected. *Unboxing* is the reverse, inserting `.intValue()`. ## The costs 1. **Memory:** each `Integer` is a separate heap object (header + the int + alignment ≈ 16 bytes) plus the reference to it, versus 4 bytes for a raw `int`. A `List<Integer>` of a million numbers uses far more memory than an `int[]`. 2. **CPU / allocation:** boxing in a tight loop creates garbage and adds indirection (you follow a pointer to read the value), hurting cache locality. In hot paths this is measurable. 3. **NPE risk:** an `Integer` can be `null`. Unboxing a `null` throws `NullPointerException` — `int x = someInteger;` blows up if `someInteger` is null. ## When it matters / alternatives For ordinary code, `List<Integer>` is fine. For large primitive collections or hot loops, use a primitive array (`int[]`), or a primitive-specialized collection library (Eclipse Collections, fastutil, Trove, HPPC) that stores `int` directly with no boxing. The JDK also offers primitive streams (`IntStream`, `LongStream`, `DoubleStream`) to avoid boxing in stream pipelines. With all this, a learner at any level can answer: *List<int> is illegal because generics require reference types (a consequence of erasure); use List<Integer> with autoboxing, and be aware of the memory/CPU/NPE costs.*
- What happens when you call nums.add(5) on a List<Integer>?The compiler autoboxes the int 5 into an Integer via Integer.valueOf(5) and stores that object reference in the list.
- Why does the language forbid List<int> rather than just supporting it?Because of type erasure: at runtime the list stores elements as Object, and a primitive int isn't an Object. Project Valhalla aims to add primitive/value-type generics in the future.
A generic container is a row of mailboxes that each hold an envelope (a reference). A primitive int is a loose coin — it doesn't fit in a mailbox until you put it in an envelope (the Integer wrapper).
saying these in an interview costs you the question
- Claiming List<int> works (it does not compile)
- Saying Integer and int are the same thing with no overhead
- Forgetting that unboxing a null Integer throws NullPointerException