The Trouble with Primitives

We just put Student objects in a DynamicArray. Our original DynamicArray, before it was generic, held int. So we might expect that we can now ask for a DynamicArray of int:

DynamicArray<int> scores = new DynamicArray<>();  // does not compile!

It does not compile. Generics work only with reference types, which is to say objects, and not with primitive types like int, double, boolean, or char.

The reason is the same erasure we saw in the constructor. When T is erased, what is left in the compiled code is Object, and our storage is an Object[]. A slot in that array holds a reference to an object. An int is not a reference to anything. It is the number itself, and it is not an object. So there is no way to put a bare int where a T is expected.

Wrapper classes

Java’s fix is a matching wrapper class for each primitive type. A wrapper class is an object that holds a single primitive value.

primitive wrapper class
int Integer
double Double
boolean Boolean
char Character

DynamicArray<int> is forbidden, but DynamicArray<Integer> is allowed:

DynamicArray<Integer> scores = new DynamicArray<>();
scores.add(95);
scores.add(88);
int first = scores.get(0);  // 95

Notice we still wrote scores.add(95) with a plain int, not scores.add(Integer.valueOf(95)). The compiler inserts the conversion between a primitive and its wrapper: it wraps an int into an Integer on the way in (autoboxing) and unwraps it back on the way out (unboxing). Most of the time you do not notice it happening.