Codectionary / Developer documentation / Java

Generics

Generics let you write classes, interfaces, and methods that work with any type while still enforcing type safety at compile time, rather than at runtime. Instead of writing a separate class for each type you want to support, a generic class like Box<T> can hold any type T, with the compiler catching type mismatches before the code ever runs. Type parameters conventionally use single uppercase letters like T, E, K, and V.

Syntax

class ClassName<T> {\n  // T can be used as a type\n}

Examples

Basic Generic Class

A class that works with any type, specified when it is used.

public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
}

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
System.out.println(stringBox.get());  // Hello

Box<Integer> intBox = new Box<>();
intBox.set(42);
System.out.println(intBox.get());  // 42

Generic Methods

A single method can be generic even inside a non-generic class.

public class Utils {
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }

    public static void main(String[] args) {
        Integer[] numbers = {1, 2, 3};
        String[] words = {"a", "b", "c"};

        printArray(numbers);
        printArray(words);
    }
}

Multiple Type Parameters

A generic class or interface can have more than one type parameter, like Map<K, V>.

public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }

    @Override
    public String toString() {
        return key + " -> " + value;
    }
}

Pair<String, Integer> pair = new Pair<>("age", 21);
System.out.println(pair);  // age -> 21

Bounded Type Parameters

Restricting a generic type to subtypes of a given class or interface with 'extends'.

public class NumberBox<T extends Number> {  // T must be Number or a subtype
    private T value;

    public NumberBox(T value) {
        this.value = value;
    }

    public double doubled() {
        return value.doubleValue() * 2;  // Number guarantees doubleValue() exists
    }
}

NumberBox<Integer> box = new NumberBox<>(10);
System.out.println(box.doubled());  // 20.0

// NumberBox<String> invalid = new NumberBox<>("text");  // Error - String isn't a Number

Best practices

  • Use generics whenever a class or method's logic is the same regardless of type, to avoid duplicating code for each specific type
  • Follow Java naming conventions for type parameters: T for a general type, E for an element, K/V for a key/value pair
  • Use bounded type parameters (<T extends Number>) when your generic code needs to call methods specific to a certain family of types
  • Prefer generics over using Object and manual casting - generics catch type errors at compile time instead of causing a ClassCastException at runtime

At a glance

Purpose
General-purpose application development
File extension
.java
Runs in
Java Virtual Machine
Usually used with
JDK and Java libraries

Specifications & further reading

Related Java documentation

Classes
Classes in Java are blueprints for creating objects, defining their properties (fields) and behaviors (methods). Java is a strictly object-oriented language where everything is encapsulated within classes. A class serves as a template that specifies what data an object will store and what operations it can perform.
Interfaces
An interface defines a contract of methods that implementing classes must provide, without specifying how those methods work internally. Unlike a class, an interface cannot be instantiated directly - it only declares what a class can do, not how. A class uses the 'implements' keyword to fulfill an interface's contract, and a single class can implement multiple interfaces, which is Java's way of achieving a form of multiple inheritance.
Abstract Classes
An abstract class is a class that cannot be instantiated directly and may contain both fully implemented methods and abstract methods (declared without a body, which subclasses must implement). Abstract classes sit between interfaces and regular classes: like an interface, they define a contract; like a regular class, they can hold state (fields) and provide shared, already-implemented behavior.
Constructors
A constructor is a special method that runs automatically when an object is created with 'new', typically used to initialize the object's fields. A constructor shares its name with the class and has no return type, not even void. Java supports constructor overloading, letting a class offer multiple ways to construct an object, and constructor chaining with this(...), where one constructor calls another in the same class.