Primitives & Wrappers
Primitive types and their wrappers, autoboxing, the Integer cache, arrays, and why Java is not purely object-oriented.
5 questions
JuniorTheoryVery commonWhat are wrapper classes, and what do autoboxing and unboxing do?
What are wrapper classes, and what do autoboxing and unboxing do?
A wrapper class such as Integer, Double, or Boolean boxes a primitive into a real object, so it can be stored in generics and collections, which hold objects only. Autoboxing converts a primitive to its wrapper automatically (e.g. int to Integer), and unboxing converts the wrapper back to a primitive, so you can mix the two without explicit conversions.
Common mistakes
- ✗Thinking primitives can be stored in collections directly, without a wrapper object
- ✗Swapping the directions: autoboxing is primitive to wrapper, unboxing is wrapper to primitive
- ✗Assuming autoboxing is free, ignoring the object allocation it performs at runtime
Follow-up questions
- →What happens when you unbox an Integer that is null?
- →Why can == compare two Integer objects unexpectedly while equals compares their values?
JuniorTheoryCommonHow does an array differ from an ArrayList in Java?
How does an array differ from an ArrayList in Java?
An array is a language construct of fixed length, exposed through a length field. It can store primitives directly, and it is covariant, so a wrong store is caught only at run time as an ArrayStoreException. An ArrayList is a library class that wraps an array and grows on demand; it exposes size() and add(), stores objects only, so primitives are boxed, and it is generic and invariant, so a wrong element type is rejected at compile time.
Common mistakes
- ✗Expecting an array to grow, when its length is fixed at the moment of creation
- ✗Mixing up the
lengthfield of an array with thesize()method of anArrayList - ✗Believing an
ArrayListcan store primitives without boxing them into wrapper objects
Follow-up questions
- →Why can an
Object[]variable hold aString[], and what breaks when you store into it? - →What does an
ArrayListdo internally once it runs out of capacity?
MiddleCodeCommonInteger caching — predict the three == comparisons
Integer caching — predict the three == comparisons
r1 is true, r2 is false, r3 is false. Autoboxing calls Integer.valueOf, which caches instances for -128..127, so 100 returns the same cached object (== true), but 200 is outside the range and boxes a fresh object each time (== false). new Integer(100) always allocates, so two new calls are never ==.
Common mistakes
- ✗Assuming
==on boxedIntegeralways compares numeric value rather than reference identity - ✗Believing the cache covers all values, not just
-128..127 - ✗Thinking
new Integer(100)participates in caching instead of always allocating
Follow-up questions
- →How can you change the upper bound of the
Integercache? - →Why was the
new Integer(int)constructor deprecated in Java 9?
SeniorTheoryCommonWhich subtle bugs can autoboxing and the Integer cache introduce?
Which subtle bugs can autoboxing and the Integer cache introduce?
Three classic ones. == between two Integer values compares references, so it is true for 127 from the -128..127 cache yet false for 128 — an equality that passes small tests and fails in production. Unboxing a null wrapper throws a NullPointerException on a line with no visible dereference, such as int x = map.get(missing). And boxing shifts overload resolution: list.remove(1) on a List<Integer> removes by index, list.remove(Integer.valueOf(1)) by value.
Common mistakes
- ✗Relying on
==between two wrappers because it happens to work for small cached values - ✗Assuming unboxing a
nullwrapper yields0rather than throwing aNullPointerException - ✗Calling
list.remove(1)on aList<Integer>and expecting it to remove the value1
Follow-up questions
- →How would you rewrite a comparison of two
Integervalues so that it is always correct? - →Why does raising
-XX:AutoBoxCacheMaxchange the result of==on boxed values?
MiddleTheoryOccasionalWhy is Java considered not a pure object-oriented language?
Why is Java considered not a pure object-oriented language?
Because it has primitive types like int, char, and boolean that are not objects: they have no methods, do not descend from Object, and live as plain values rather than on the heap. A pure OO language would treat everything as an object. Java keeps primitives for performance and bridges them into the object world with wrapper classes like Integer.
Common mistakes
- ✗Attributing the impurity to standalone functions, which Java does not actually allow outside classes
- ✗Claiming primitives extend Object and have methods, when they are plain non-object values
- ✗Forgetting that wrapper classes exist precisely to bridge primitives into the object world
Follow-up questions
- →Why did Java keep primitive types instead of making every value an object?
- →How do generics force you to use wrapper types rather than primitives?