Skip to main content

Command Palette

Search for a command to run...

Generics in Java

Updated
3 min readView as Markdown

Generics provide flexibility to methods and data structures by reducing code duplication and enhancing code reusability.
They also provide compile-time type safety.


Advantages of Generics

  • ✅ Type safety – prevents inserting the wrong type.

  • ✅ Code reusability – write once, use with multiple types.

  • ✅ Elimination of explicit type casting.


Generic Class

public class Box<T> {
    private T content;

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

    public T getContent() {
        return content;
    }
}

public class Main {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        intBox.addContent(42);
        System.out.println(intBox.getContent());
    }
}

Generic Method

public class Util {
    public <E> void printArray(E[] array) {
        for (E element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

public class Main {
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] stringArray = {"Apple", "Banana", "Orange"};

        Util util = new Util();
        util.printArray(intArray);
        util.printArray(stringArray);
    }
}

Wildcards in Generics (?)

public class Printer {
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
}
  • ? → Represents any type (unknown type).

Bounded Generics

  • Upper Bound (extends) → accepts a type and all its subclasses.

  • Lower Bound (super) → accepts a type and all its superclasses.

public class NumberBox<T extends Number> {
    private T content;

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

    public T getContent() {
        return content;
    }
}

Examples:

  • ? extends T → Any subtype of T

  • ? super T → Any supertype of T


Generic Interface

public interface Pair<K, V> {
    K getKey();
    V getValue();
}

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

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

    @Override
    public K getKey() {
        return key;
    }

    @Override
    public V getValue() {
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        Pair<Integer, String> p1 = new OrderedPair<>(1, "One");
        System.out.println(p1.getKey() + " = " + p1.getValue());
    }
}

Wrapper Classes & Generics

  • Generics do not support primitive types (e.g., int, char).

  • Instead, Java uses wrapper classes (Integer, Character, etc.) because generics work with objects stored on the heap, not primitives stored on the stack.


Type Erasure

Java generics use type erasure for backward compatibility.

  • During compilation:

    • The compiler checks type correctness.

    • Prevents errors (e.g., inserting a String into List<Integer>).

  • During erasure:

    • Generic types are replaced with their bounds (or Object if unbounded).

    • Compiler inserts type casts where necessary.

Bridge Methods

  • If a subclass overrides a generic method, the compiler may create bridge methods to preserve polymorphism after type erasure.

Generics Limitations

  1. Arrays and Generics don’t mix well.

    • Arrays know their element type at runtime.

    • Generics are erased at runtime → causes conflicts.

  2. Unchecked warnings may occur when type safety cannot be fully checked at runtime.