Dataslope logoDataslope

Generic Classes and Methods

How to declare your own generic types and methods, and the small-but-deep set of design choices that come with them

We have used the standard generic types (List<E>, Map<K,V>, Set<E>). It is time to write our own. Designing a generic class is one of the most underrated skills in Java — it is what separates a library you can use anywhere from one you have to copy-paste and edit.

A first generic class: a typed box

The traditional "hello world" of generics is a Box<T> — a container of exactly one element of some type T. Compare the untyped and typed versions.

Code Block
Java 8 (Update 492)

Three things to notice in the typed Box:

  • The type parameter <T> appears immediately after the class name. From inside the class, T is just a placeholder type — wherever you would write String or Customer, you can write T.
  • put and get use T. The compiler now enforces that whatever put accepts is whatever get returns.
  • The diamond <> on new Box<>() lets the compiler infer String from the variable's declared type.

Naming conventions

By convention, single uppercase letters name type parameters:

LetterMeaning
EAn element of a collection
TA general type — the only or main type parameter
K, VA key and a value in a map
RA return type, especially in functional interfaces
NA numeric type
S, USecondary type parameters when more than one is needed

These are conventions, not rules — the compiler will accept any identifier — but every Java reader is wired to expect them, so straying is gratuitous noise.

Multiple type parameters

A generic class can take more than one type parameter. The classic example is Pair<A, B>:

Code Block
Java 8 (Update 492)

Each occurrence is checked independently — putting a String where a B is expected only works if B = String for that particular instance.

Generic methods (independent of the class)

A method can have its own type parameters, independent of the enclosing class. The type parameter declaration goes before the return type:

public static <T> T identity(T x) { return x; }
Code Block
Java 8 (Update 492)

Generic methods are how the standard library writes things like Collections.emptyList() or Map.of(k, v). You usually do not have to specify the type parameter at the call site — the compiler infers it.

A multi-file generic class: a stack

This is the classic generic class to write next. Stack<E> is a typed LIFO container — push, pop, peek, size.

Code Block
Java 8 (Update 492)

Compare this to the JDK's legacy Stack: ours is generic from line one, has no thread-safety penalty, and does not leak random Vector methods into its public API. This is what programming to interfaces

  • generics buys you.

A generic class with a constraint: numeric averages

Sometimes a type parameter must support some operation. For example, a generic average method needs the elements to be Numbers. That is what bounded type parameters are for, in their simplest form:

Code Block
Java 8 (Update 492)

We will go far deeper into bounds and wildcards in the next chapter. For now, the syntax <T extends Number> says "any T that is a Number."

Constraints can also be on interfaces

extends works for interfaces too. A common case is requiring the type to be Comparable:

Code Block
Java 8 (Update 492)

You can also combine constraints with &: <T extends Number & Comparable<T>> means "any T that is both a Number and Comparable<T>."

Practice

Challenge
Java 8 (Update 492)
A generic Pair with swap()

Write a class Pair<A, B> with:

  • A constructor that takes A first and B second.
  • Accessors first() and second().
  • A method Pair<B, A> swap() that returns a new pair with the components reversed (note the type change!).

Expected output:

ada / 36
36 / ada

Test your understanding

QuestionSelect one

Where does a generic method declare its own type parameters?

After the method name

In the class header

Immediately before the return type, e.g. public static <T> T identity(T x)

In the calling code only

QuestionSelect one

What does the bound <T extends Number> allow the method body to do that <T> would not?

Call any method of Object on T

Call methods defined on Number (such as intValue(), doubleValue()) on any value of type T

Compare T values with < and >

Construct new instances of T

QuestionSelect one

Why is class Stack<E> { private final List<E> data = new ArrayList<>(); … } a better design than the legacy java.util.Stack?

It uses less memory

It composes a List instead of extending Vector, so its public API contains only the stack operations — not random list methods

It is faster on every operation

The legacy Stack does not compile in modern Java

On this page