Generic Classes in Java

Naveen AnamaneniUpdated · 4 min read
mediumJava 25javagenerics

A courier box can carry a book, a phone or a sweet box; the box doesn't care what's inside, but the label must say what it holds. A generic class is that box in code. You write the class once, with a placeholder type like T, and each time someone uses it they fill in the real type: a Box<String>, a Box<Integer>. The compiler then checks every use against that label.

Writing a Generic Class

Write Box<T> once; use it with any type
Boxes.java▶ Try it
public class Boxes {
    static class Box<T> {
        private T value;
 
        Box(T value) { this.value = value; }
 
        T get() { return value; }
        void put(T value) { this.value = value; }
        boolean isEmpty() { return value == null; }
    }
 
    public static void main(String[] args) {
        Box<String> name = new Box<>("Navika");
        Box<Integer> marks = new Box<>(91);
 
        String upper = name.get().toUpperCase();
        int bonus = marks.get() + 5;
        System.out.println(upper + " " + bonus);
 
        marks.put(95);
        System.out.println(marks.get() + " " + name.isEmpty());
    }
}
Output
NAVIKA 96
95 false

How it works

  1. class Box<T> declares a type parameter T right after the class name.
  2. Inside the class, T is used like any type: the field T value, the parameter of put, the return type of get.
  3. Box<String> fills T with String. For that box, get() returns a String and put accepts only a String.
  4. The same class serves Box<Integer>, with no new code.

Several Type Parameters

A class can have as many type parameters as it needs, separated by commas:

Pairs.java▶ Try it
public class Pairs {
    record Pair<K, V>(K key, V value) {
        @Override
        public String toString() { return key + " = " + value; }
    }
 
    public static void main(String[] args) {
        Pair<String, Integer> marks = new Pair<>("Srihitha", 88);
        Pair<String, Boolean> passed = new Pair<>("Srihitha", true);
        System.out.println(marks);
        System.out.println(passed.value() ? "passed" : "failed");
    }
}
Output
Srihitha = 88
passed

Pair<K, V> is a generic record: records can take type parameters just like classes. K and V are filled independently, so Pair<String, Integer> and Pair<String, Boolean> are both possible.

Common Mistakes

Calling type-specific methods on T. Inside Box<T>, you can only call Object methods on value, because T could be anything.

Using T in a static field or method of the class. Each instance can have a different T, so static members can't use it (see "Restrictions of Generics in Java").

Creating new T(). The type isn't known at runtime; pass a Supplier<T> instead.

Practice

  1. Easy: Write class Wallet<T> holding a list of items of type T, with add(T) and count(). Use it as Wallet<String> for coupon codes.
  2. Harder: Write a generic Result<T> record with a T value and a String error, plus static factories ok(T value) and fail(String error). Return it from a method that parses a number.

Quick check: Generic classes

4 questions
1Where is a class's type parameter declared?
2In Box<String>, what does get() return?
3Can a generic class have several type parameters?
4Inside Box<T>, which methods can you call on a T value?

Key Takeaways

Was this tutorial helpful?

Lesson 239 of 482