When to use primitive and when reference types in Java

That really depends on the context. First prefer the primitive, because it's more intuitive and has less overhead. If it is not possible for generics/autoboxing reasons, or if you want it to be nullable, then go for the wrapper type (complex type as you call it).


The general rules I follow when creating an API can be summarized as follows:

  1. If the method must return an value, use a primitive type
  2. If the method may not always apply (eg: getRadioId(...) on an object where such an ID may not exist), then return an Integer and specify in the JavaDocs that the method will return null in some cases.

On #2, look out for NPEs when autoboxing. If you have a method defined as:

public Integer getValue();

And then call it as follows:

int myValue = getValue();

In the case where getValue() returns null you'll get an NPE without an obvious cause.


Since Java does something called auto-boxing and auto-unboxing, you should use the primitive type int in most cases because of less overhead.

The only time you absolutely need to use Integer is in generics.

List<int> list; // won't compile
List<Integer> list; // correct

In which case should you use primitive types(int) or reference types (Integer)?

As a rule of thumb, I will use a primitive (such as int) unless I have to use a class that wraps a primitive.

One of the cases were one must use a wrapper class such as Integer is in the case of using generics, as Java does not support the use of primitive types as type parameters:

List<int> intList = new ArrayList<int>();               // Not allowed.
List<Integer> integerList = new ArrayList<Integer>();   // Allowed.

And, in many cases, I will take advantage of autoboxing and unboxing, so I don't have to explicitly perform conversions from primitives to its wrapper class and vice versa:

// Autoboxing will turn "1", "2", "3" into Integers from ints.
List<Integer> numbers = Arrays.asList(1, 2, 3); 

int sum = 0;

// Integers from the "numbers" List is unboxed into ints.
for (int number : numbers) {
  sum += number;
}

Also, as an additional note, when converting from primitives to its wrapper class objects, and unique instances of objects are not necessary, use the valueOf method provided by the wrapper method, as it performs caching and return the same instance for a certain value, reducing the number of objects which are created:

Integer i1 = Integer.valueOf(1);   // Prefer this.
Integer i2 = new Integer(1);       // Avoid if not necessary.

For more information on the valueOf methods, the API specification for the Integer.valueOf method can serve as a reference for how those methods will behave in the wrapper classes for primitives.