Concepts / Creating Reusable Code with Generic Classes and Methods

Creating Reusable Code with Generic Classes and Methods

A type parameter is a placeholder written in angle brackets after a class name, and it works much like a formal parameter in a subroutine.

  • Programming
Interactive lab

Try it: Stack and Queue

The difference between a stack (last in, first out) and a queue (first in, first out).

How it works

  1. A stack adds (push) and removes (pop) at the same end, the top — the last item added leaves first.
  2. A queue adds (enqueue) at the back and removes (dequeue) from the front — the first item added leaves first.
  3. peek / front look at the next item without removing it.
  4. Adding to a full structure is an overflow; removing from an empty one is an underflow.

Default run (3 steps): Stack (bottom → top): 4, 7. 2 of 8 slots used. Choose an operation. Example: push 9. … Stack is now: 4, 7, 9. Size 3/8.

Simplified: Fixed capacity of 8 items so overflow can be shown; Python lists and collections.deque grow as needed.

Educational simulation

Loading the simulation…

Why One Queue Class Is Not Enough

Suppose you want a queue that can hold strings, integers, or colors. Writing a separate queue class for each element type would repeat nearly the same code again and again. A generic class solves this problem by leaving one spot in its definition open. That open spot is called a type parameter, and the class is written so that the same code works no matter which real type fills the spot.

EASY

Before you read the explanation, look at what a Queue class asks of its placeholder. Which parts of the class would need a real type name, and which parts of the code would be exactly the same for every element type? Keep your answer in mind as you work through the rest of this article.

Hints
  • Think about what a queue stores, and what goes in and out of it.
  • Ask whether the logic for adding and removing items depends on what the items are.

Tracing the Queue Body Before Any Type Is Chosen

The class below is written in terms of a placeholder named ItemType. The placeholder is declared in angle brackets right after the class name. Until someone uses the class, ItemType does not stand for any particular type. Read the body as a set of promises about an unknown element type.

java
Notes on the code
  • Queue<ItemType> declares the type parameter in angle brackets after the class name
  • LinkedList<ItemType> uses the placeholder as the actual type parameter of another generic class
  • enqueue takes a formal parameter item whose type is the placeholder
  • dequeue returns the placeholder type

Trace the four jobs the placeholder does in this code. First, it is the return type of dequeue. Second, it is the type of the formal parameter item in enqueue. Third, it is the actual type parameter given to the LinkedList that stores the items, so the internal list is declared as a LinkedList of the placeholder type. Fourth, and less obvious, the class is written only in terms of the placeholder, so it never commits to any particular element type. Every time you see ItemType in the body, you should treat it the same way you would treat String or int, even though it is still an unknown.

  • Return type of dequeue: ItemType
  • Type of the formal parameter item in enqueue: ItemType
  • Actual type parameter of the internal LinkedList: ItemType
  • Element type committed to by the class: none, because the class uses only the placeholder

How a Placeholder Becomes an Actual Type

A type parameter works much like a formal parameter in a subroutine. A subroutine's formal parameter is a stand-in that receives a real value at each call. A type parameter is a stand-in that receives a real type name at each use of the class. The table below lines up the two ideas so you can see where they match and where they differ.

AspectFormal parameter of a subroutineType parameter of a generic class
Where it is declaredIn the parameter list of the subroutineIn angle brackets after the class name
What fills it inA real value, supplied at each callA real type name, supplied at each use of the class
What it stands forA value such as 5 or a stringA type such as String or Integer
Example of a real fillingPassing 5 to a method that takes an intWriting Queue<String> to create a queue of strings

Type parameters and formal parameters are both placeholders, but they receive different kinds of things.

The placeholder is replaced by an actual type name when the class is used to declare a variable or create an object. Because of this, one class definition gives you a whole family of parameterized types. The same Queue class can be used as Queue<String>, Queue<Integer>, or Queue<Color>. Queue is used with the same angle-bracket notation as the built-in generic library classes LinkedList and HashSet, so once you know how to read one, you can read the other.

java
Output
first now refers to the string Ada
Notes on the code
  • The declaration fills the placeholder with String
  • enqueue now accepts a String as its formal parameter
  • dequeue now returns a String, so the result needs no cast

Nothing in the Queue class itself was rewritten to make this happen. The substitution is applied at the point of use. The diagram below shows the three places the placeholder appeared in the class body and what each one becomes once the class is used as Queue<String>.

ItemTypereturn type of dequeueStringreturn type of dequeueItemTypeformal parameter of enqueueStringformal parameter of enqueueLinkedList<ItemType>internal storageLinkedList<String>internal storagereplaced byreplaced byreplaced by
What does the compiler substitute for ItemType in each place it appears when the class is used as Queue<String>, and what does the class look like afterward?
Worked example

Tracing the substitution for Queue<String>

The declaration is Queue<String> names. Determine the type of each use of the placeholder inside the Queue class once the class is used this way.

  1. 1
    Find the declaration

    The use Queue<String> fills the placeholder ItemType with String.

  2. 2
    Check dequeue

    Its return type was ItemType, so it becomes String. This is why String first = names.dequeue() needs no cast.

  3. 3
    Check enqueue

    Its formal parameter item was ItemType, so it now accepts a String. The call names.enqueue("Ada") matches.

  4. 4
    Check the internal list

    The field was LinkedList<ItemType>, so it becomes LinkedList<String>. The stored items are strings.

Result

Every use of ItemType in the body now means String. The class code is unchanged; only the type filling the placeholder differs.

Tracing a Type Mismatch Through the Class

Once the placeholder has been replaced, every value that flows into or out of the class must agree with that type. A type-parameter mismatch appears at the step where a value of one type reaches a place declared with a different type. Trace the flow step by step to find it. The diagram below follows a value of type Integer as it is offered to a queue declared as Queue<String>.

names :Queue<String>declared typeenqueue(42)call passes an Integeritem : Stringformal parameter aftersubstitutionInteger vs Stringmismatch found heremethod callvalue enters parametertypes compared
When a value of one type flows into a method or field declared with a different type parameter, at which step does the mismatch appear?

Read the diagram from top to bottom. The declared type Queue<String> fixes the placeholder as String. The value 42 arrives at enqueue, whose formal parameter is now a String. The mismatch is therefore found at the call, where the Integer value meets the String parameter, not inside the body of enqueue itself. This is the place to look when a generic class seems to behave incorrectly: check which type was substituted at the declaration, then check the type of each value that crosses the boundary.

Choosing a Name for the Type Parameter

The name of a type parameter is arbitrary. Renaming ItemType to T has no effect on the meaning of the class or on how it is used. The conventional letter T is common in generic code, but it is not required. The name is only a label used inside the class, and it is replaced by an actual type name at each point of use. Because the name changes nothing about behavior, your choice should be guided by how easily a reader can follow the code.

Mistakes Beginners Make with Type Parameters

  • Common mistake

    Thinking the class body must be rewritten for each element type

    Why it fails

    The class is written only in terms of the placeholder, so the same code works for every element type.

    Do this instead

    Write the class once with the placeholder, then use it with different real types such as Queue<String> and Queue<Integer>.

  • Common mistake

    Treating ItemType as a real class that exists somewhere in the program

    Why it fails

    The type parameter is a stand-in that receives a real type name at each use. It does not name a type of its own.

    Do this instead

    Read ItemType as a blank to be filled in. When the class is used as Queue<String>, read every ItemType as String.

  • Common mistake

    Believing that renaming the type parameter changes what the class does

    Why it fails

    The name of a type parameter is arbitrary, so renaming ItemType to T has no effect on the meaning of the class.

    Do this instead

    Choose a name that is clear to readers, and keep the same name consistent throughout the class.

  • Common mistake

    Passing a value of the wrong type into a parameterized object

    What it looks like
    Queue<String> names followed by names.enqueue(42)
    Why it fails

    After substitution, the formal parameter item is a String, so an Integer value does not match it.

    Do this instead

    Pass a String, or declare the queue as Queue<Integer> if you need to store integers.

  • Common mistake

    Forgetting that the placeholder also appears inside other generic types, such as LinkedList<ItemType>

Practice Tracing a Box Class

MEDIUM

A generic class is declared as Box<Contents>. It has a method void put(Contents c) and a method Contents get(). A learner writes Box<Color> crate. Trace the types: what type does put accept, and what type does get return? Then identify which line would show a mismatch if the learner called crate.put("red").

Hints
  • Find the placeholder in the angle brackets and note what replaces it in Box<Color>.
  • Apply the substitution to the formal parameter of put and to the return type of get.
  • A String value does not match a Color parameter.

Key Takeaways

  • A type parameter is a placeholder written in angle brackets after a class name, and it works much like a formal parameter in a subroutine.
  • Inside a generic class, the placeholder can serve as a return type, a parameter type, and an actual type parameter of another generic class such as LinkedList.
  • When a variable is declared or an object is created, the placeholder is replaced by an actual type such as String, so one class definition serves many element types.
  • The name of a type parameter is arbitrary and does not change the meaning of the class, so choose a name that readers can follow.
  • To find a type-parameter mismatch, identify the substituted type at the declaration, then check the type of each value that crosses into the class.