Concepts / Object Types and Class Names

Object Types and Class Names

ArrayList<T> is a parameterized type: a single class ArrayList with a type parameter slot that can be filled to form many distinct types such as ArrayList<String> and ArrayList<Color>.

  • Programming

Three Lists, One Class

Suppose one part of a program needs a growable list of names, another needs a growable list of colors, and a third needs a growable list of players in a game. A naive approach would be to write three separate list classes, each identical except for the kind of element it stores. That duplication gets worse as the number of element types grows.

Java offers a better path. A single ArrayList class can be specialized by naming the kind of element it holds. Once you understand how that specialization is written, the same class can serve every one of those needs. This article follows one question: how does a single class produce many distinct types, and what rules govern which types you are allowed to produce?

Reading ArrayList<String> Piece by Piece

The notation ArrayList<String> looks unusual at first, because it seems to contain two names in one. It is best read as two pieces. The part before the angle brackets, ArrayList, names the class. The part inside the angle brackets, String, is a type argument. It tells Java what kind of element this particular ArrayList is meant to hold.

ArrayListclass name<opens the type slotStringtype argument>closes the type slotfills slot with
Which part of ArrayList<String> is the class name, which part is the type parameter, and what role does each part play?

Read left to right, the expression says: take the ArrayList class and fill its type slot with String. The angle brackets are the notation for that filling. Because the slot is what varies, the class name stays the same across every version of the type. The whole expression names a type that is more specific than the bare class name ArrayList.

One Template, Many Types

A useful way to think about this is that the class is a template, and each filled-in form is a concrete type you can actually use. The template is written once. The filled-in types are produced by choosing a type argument. This is why the list of possible types looks large while the number of class definitions stays at one.

ArrayList<T>one class, written onceArrayList<String>list of namesArrayList<Color>list of colorsArrayList<Player>list of game playersfill T with Stringfill T with Colorfill T with Player
How does a single class definition ArrayList<T> become separate types like ArrayList<String> and ArrayList<Color>, and which parts stay the same across them?

The part that stays the same is the class ArrayList, defined in the package java.util, along with its behavior as a growable list. The part that varies is the type argument placed in the slot. Each choice gives a different type, even though all of them come from one class definition.

What do you think happens?

In the type ArrayList<Color>, what is the class name, and what is the type argument?

Reveal answer
Answer:

The class name is ArrayList, and the type argument is Color.

The part before the angle brackets names the class. The part inside the angle brackets is the type argument that fills the type slot.

Which Type Arguments Are Legal

Not every type can fill the slot. The type parameter must be an object type, such as a class name or an interface name. Primitive types such as int and char cannot fill the slot. So the question to ask about any candidate type argument is whether it names an object type or a primitive type.

Candidate type argumentKind of typeLegal in ArrayList<...>?
Stringclass name (object type)Yes
Colorclass name (object type)Yes
intprimitive typeNo, primitives cannot fill the slot
charprimitive typeNo, primitives cannot fill the slot

Deciding whether a type argument is legal depends on whether the candidate is an object type.

Declarations and Matching Object Creation

Once a parameterized type has been formed, it behaves like any other type. It can be used to declare variables, and it can be used with the new operator to create objects. The example below uses both: the first line declares a variable whose type is ArrayList<String>, and the second creates a new object of that type and stores it in the variable.

java
Notes on the code
  • ArrayList<String> on the left declares the variable names with a parameterized type
  • new ArrayList<String>() on the right creates an object of that same type
  • The type argument String appears on both sides and must agree
Worked example

Checking whether a declaration and creation agree

A programmer writes ArrayList<String> names = new ArrayList<Color>(); Decide whether this statement is valid and explain why.

  1. 1
    Identify the declared type

    The left side declares a variable of type ArrayList<String>, so the type argument is String.

  2. 2
    Identify the created type

    The right side creates an object of type ArrayList<Color>, so the type argument is Color.

  3. 3
    Compare the type arguments

    The two type arguments differ. Java requires the declaration and the object creation to agree when they are written together.

  4. 4
    Conclude

    The statement is invalid and produces a compile-time error. Changing either side to match the other fixes it.

Result

The statement is illegal because String and Color do not match. Writing ArrayList<String> on both sides makes it valid.

Parameters and Return Types

A parameterized type is not limited to local variables. It can also appear as the type of a formal parameter in a subroutine definition, and it can be the return type of a subroutine. In both roles it describes what the subroutine expects to receive or produce, so the caller and the subroutine share a precise description of the list involved.

VariabledeclarationArrayList<String> namesFormal parameterArrayList<Color> colorsReturn typeArrayList<Color>Caller receives listsame declared typepassed as argumentsubroutine returnsassigned to variable
How does a parameterized type like ArrayList<String> flow through a variable declaration, into a method parameter, and back out as a return value?
java
Notes on the code
  • ArrayList<Color> after the parameter list is the formal parameter type
  • ArrayList<Color> before the method name is the return type
  • Both describe the same parameterized type, so the caller and the method agree on the list

Mistakes Beginners Make

  • Common mistake

    Reading ArrayList<String> as two unrelated names

    What it looks like

    Assuming String is a second class that ArrayList is somehow mixed with.

    Why it fails

    The part before the angle brackets names the class, and the part inside is a type argument that fills the type slot.

    Do this instead

    Read the expression as the class ArrayList with its type slot filled by String.

  • Common mistake

    Thinking each parameterized type needs its own class

    What it looks like

    Writing a separate NameList class and a separate ColorList class.

    Why it fails

    A single ArrayList class with a type parameter slot produces many distinct types.

    Do this instead

    Write the class once and choose the type argument when you use it.

  • Common mistake

    Using a primitive type as the type argument

    What it looks like
    ArrayList<int>
    Why it fails

    The type argument must be an object type, and primitive types such as int and char cannot fill the slot.

    Do this instead

    Use an object type such as ArrayList<Integer>, where Integer is a class name.

  • Common mistake

    Changing the type argument on only one side of an assignment

    What it looks like
    ArrayList<String> names = new ArrayList<Color>();
    Why it fails

    The declaration and the object creation must match exactly when they are written together.

    Do this instead

    Make both type arguments identical, such as ArrayList<String> on both sides.

  • Common mistake

    Forgetting that a parameterized type works in parameters and return types, not only in local variables

Practice Reading Parameterized Types

EASY

For each type below, name the class and the type argument, then say whether the type argument is legal: ArrayList<Player>, ArrayList<char>, and ArrayList<String>.

Hints
  • Split each expression at the angle brackets.
  • Ask whether the candidate type argument is an object type or a primitive type.
  • Only object types such as class names can fill the slot.
MEDIUM

Write a declaration and an object creation for a list of colors, then a method signature that takes such a list as a parameter and returns it. Make sure the type argument matches in every place it appears.

Hints
  • Use Color as the type argument for every ArrayList you write.
  • The declaration and the new expression must use the same type argument.

Key Takeaways

  • ArrayList<String> is a parameterized type: the class name ArrayList stays fixed, and the type argument String fills the type slot.
  • One class definition, ArrayList<T>, produces many distinct types such as ArrayList<String>, ArrayList<Color>, and ArrayList<Player>.
  • The type argument must be an object type such as a class or interface name; primitive types such as int and char cannot fill the slot.
  • A parameterized type can declare variables, create objects with new, describe formal parameters, and serve as a return type, and the type arguments must match exactly when written together.