Concepts / Generic Classes in Java

Generic Classes in Java

Generic programming is a paradigm that produces highly general and reusable code, meaning code not tied to one narrow situation.

  • Programming

From Using Generic Classes to Writing Them

So far you have worked with generic classes and generic methods that already exist. The Java Collection Framework supplies many of them, and you have been using them as finished tools. This material begins a shift in perspective. Instead of only consuming generic code, you are now going to look at why someone would want to write new generic classes and methods from scratch, and what that effort buys.

The central question is simple: what is the payoff? A useful way to hold that question in mind is to ask, as you read, what a piece of code would have to look like if it needed to keep working across many different situations without being rewritten each time.

Generic programming is a paradigm that produces highly general and reusable code. General code is code that is not tied to one narrow situation. Reusable code is code that can be used again in many settings. Keep those two properties in mind, because the rest of this article explains why they matter and who gains from them.

One Class, Many Concrete Types

Consider the alternative first. Without a general approach, a programmer solving a similar problem for several different situations may end up writing several nearly identical pieces of code, each one locked to its own situation. Generic programming pushes in the opposite direction, toward a single piece of code that serves many situations.

A generic class handles this by introducing a type parameter, written inside angle brackets after the class name. In the class Box<T>, the letter T is a placeholder. It does not name a real type yet. A real type is supplied later, at the point where the class is used, so the same class definition can become Box<Integer> in one place and Box<String> in another.

Box<T>T is a placeholderBox<Integer>T filled in as IntegerBox<String>T filled in as Stringused with Integerused with String
What changes when the type parameter T is filled in with Integer or with String, and what stays the same?

Read the diagram from the top down. The class definition, Box<T>, is written once. Each use fills the placeholder with a concrete type. What changes between the two after-states is only the type that T stands for. What stays the same is the structure of the class: the same fields, the same methods, and the same logic. That shared structure is what makes the code general.

A Small Generic Box

The following example is an original illustration written for this article. It defines one generic class and uses it twice, once with Integer and once with String. Read it with the diagram above in mind: the class body is written once, and the main method supplies the concrete types.

java
Output
43
5
Notes on the code
  • Box<T> declares the type parameter T after the class name
  • private T value uses T as the type of the field
  • set(T value) and T get() use T in method signatures
  • Box<Integer> and Box<String> fill T with two different concrete types

What do you think happens?

Before reading on, what does the program print on its first line, and why?

  • 42, because intBox stores the value 42 unchanged

  • 43, because get() returns an Integer and 1 is added to it

  • A compile error, because one class cannot hold two types

Reveal answer
Answer:

43, because get() returns an Integer and 1 is added to it

intBox was parameterized with Integer, so intBox.get() returns an Integer value of 42, and adding 1 produces 43. The same class body, with T filled in as String, served textBox, which is why the second line prints 5, the length of hello.

Tracing a Generic Box Through Its Methods

Worked example

Following the Type Parameter Through One Use

Trace what happens to the type parameter T when the code creates a Box<String> and stores a value in it.

  1. 1
    Declare the use

    Box<String> textBox fills the placeholder T with String. Inside this use of the class, every place where T appears now stands for String.

  2. 2
    Create the object

    new Box<>() builds a box. The field declared as private T value now holds a slot whose type is String for this object.

  3. 3
    Call set

    textBox.set("hello") passes a String, which matches the parameter type T as filled in for this object. The field value now refers to hello.

  4. 4
    Call get

    textBox.get() returns the stored value with the type String, so calling length() on it is valid and returns 5.

Result

The same class body handled the String case without any change to its code. Only the type filled into T differed from the Integer case.

Notice what this trace did not require. You did not write a separate StringBox class with its own set and get methods. The generic class carried the logic once, and each use supplied the type that the logic should work with. This is the practical meaning of highly general code: one definition, many situations.

Reading the Type Parameter in a Signature

Generic syntax is what you will meet when you read Javadoc. Javadoc is the documentation that describes Java's standard classes, and the standard generic classes are part of that material. In a signature such as Map<K,V>, the angle brackets contain the type parameters. Each name inside them is a placeholder that a user of the class will later replace with a real type.

Mapthe generic type name<opens the type parameterlistKfirst type parameterVsecond type parameter>closes the type parameterlist
In Map<K,V>, which tokens are the generic type name, and which tokens form the list of type parameters?

Reading a signature is a matter of separating two roles. The name before the angle brackets identifies the generic type. The names inside the brackets are its type parameters, and each one stands for a type that is chosen when the generic type is used. When you see a signature like this in documentation, the question to ask is which concrete types the user must supply in those slots.

Who Benefits and Why

The source identifies a specific group that benefits most directly from generic programming: people who write reusable software libraries. A software library is a collection of code that other programmers can call on, and its value depends on how many situations it can serve. A library author who writes general, reusable code gives every future user of that library a broader set of options.

IntBoxworks only with IntegervaluesBox<T>works with any type chosenat useStringBoxworks only with Stringvalues
How does one generic implementation serve many data types, compared with writing a separate narrow class for each type?

The source makes a careful distinction here. Not every programmer needs to write reusable software libraries, but every programmer should know at least a little about how to do it. The motivation is not that everyone must become a library author. The point is that a basic understanding of the idea is valuable even to programmers who will never publish a library.

There is also a practical reason that reaches everyone. Reading the Javadoc documentation for Java's standard generic classes requires knowing some of the generic syntax introduced in this material. So even a programmer who only uses library classes gains from understanding how type parameters are written and what they mean.

Mistakes When Reading Generic Code

  • Common mistake

    Treating T as a real class named T

    What it looks like

    Looking for a file or class called T when reading Box<T>

    Why it fails

    T is a placeholder. It is filled in with a concrete type at the point where the class is used.

    Do this instead

    Read the name inside the angle brackets as a slot that a user will fill, and ask what type goes there in each use.

  • Common mistake

    Assuming Box<Integer> and Box<String> need separate class definitions

    What it looks like

    Writing IntBox and StringBox with duplicate set and get methods

    Why it fails

    The generic class body is written once and serves both uses, which is the source of its generality.

    Do this instead

    Keep one generic definition and let each use supply its type.

  • Common mistake

    Skipping the generic syntax when reading Javadoc

    What it looks like

    Ignoring the <K,V> part of Map<K,V> in the documentation

    Why it fails

    The source notes that reading the Javadoc for standard generic classes requires knowing some of this syntax.

    Do this instead

    Identify the type parameter list first, then match each name to the role it plays in the class or method.

Practice: Tracing a Type Parameter

EASY

Using the Box<T> class from the example, explain in your own words what the single class definition contributes and what each use contributes. Then state what type the compiler knows textBox.get() returns, and why that knowledge matters when you call a method on it.

Hints
  • Separate the class body, which is written once, from the concrete type supplied at each use.
  • Look at which methods mention T and what they promise to return or accept.
  • Think about what would go wrong if get() returned an unknown type instead.
MEDIUM

Read the signature Map<K,V> and write one sentence that names the generic type, one that names its type parameters, and one that says which concrete types a user must choose when using it.

Hints
  • The generic type name appears before the angle brackets.
  • The type parameters are the names inside the angle brackets.

Key Takeaways

  • Generic programming is a paradigm that produces highly general and reusable code, meaning code not tied to one narrow situation.
  • A generic class declares a type parameter, such as T in Box<T>, that is a placeholder filled in with a concrete type at each use, so one class body serves many types.
  • Writers of reusable software libraries benefit most directly from generic programming, but every programmer should know at least a little about it.
  • Reading Javadoc for Java's standard generic classes, such as Map<K,V>, requires recognizing the type parameter syntax.