Java Generics

Generics let you write classes and methods which work with many types, and still keep the type safe. You already use them in List<String> and Map<String, Integer>. The type in the angle brackets is a type parameter.

Why generics?

Without generics, a list holds Object, and you must cast. Mistakes show only when the program runs:

import java.util.ArrayList;
import java.util.List;

public class Raw {
    public static void main(String[] args) {
        List names = new ArrayList();      // raw type, no generics
        names.add("Riya");
        names.add(42);                     // no error here
        String first = (String) names.get(1);
    }
}
Output

Raw.java:7: warning: [unchecked] unchecked call to add(E) as a member of the raw type List
names.add("Riya");
^
where E is a type-variable:
E extends Object declared in interface List
Raw.java:8: warning: [unchecked] unchecked call to add(E) as a member of the raw type List
names.add(42); // no error here
^
where E is a type-variable:
E extends Object declared in interface List
2 warnings
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at Raw.main(Raw.java:9)

With generics the compiler stops the mistake early:

import java.util.ArrayList;
import java.util.List;

public class Typed {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Riya");
        names.add(42);
    }
}
Output

Typed.java:8: error: no suitable method found for add(int)
names.add(42);
^
method List.add(String) is not applicable
(argument mismatch; int cannot be converted to String)
method List.add(int,String) is not applicable
(actual and formal argument lists differ in length)
1 error
error: compilation failed

A generic class

class Box<T> {
    private T value;

    Box(T value) {
        this.value = value;
    }

    T get() {
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        Box<String> a = new Box<>("Java");
        Box<Integer> b = new Box<>(25);
        String s = a.get();            // no cast needed
        int n = b.get();
        System.out.println(s + " " + n);
    }
}
Output

Java 25

A generic method

import java.util.Arrays;

public class Method {
    static <T> void printAll(T[] items) {
        for (T item : items) {
            System.out.print(item + " ");
        }
        System.out.println();
    }

    static <T> T firstOf(java.util.List<T> list) {
        return list.get(0);
    }

    public static void main(String[] args) {
        printAll(new Integer[]{1, 2, 3});
        printAll(new String[]{"a", "b"});
        System.out.println(firstOf(Arrays.asList("x", "y")));
    }
}
Output

1 2 3
a b
x

Bounded types

Use extends to limit the type. T extends Comparable<T> means “T must be comparable”, so we can use compareTo:

import java.util.List;

public class Bound {
    static <T extends Comparable<T>> T max(List<T> list) {
        T best = list.get(0);
        for (T item : list) {
            if (item.compareTo(best) > 0) {
                best = item;
            }
        }
        return best;
    }

    public static void main(String[] args) {
        System.out.println(max(List.of(3, 9, 4)));
        System.out.println(max(List.of("pear", "apple", "zebra")));
        System.out.println(max(List.of(2.5, 1.5)));
    }
}
Output

9
zebra
2.5

Wildcards

? means “any type”. ? extends Number accepts a list of Number or any subclass:

import java.util.List;

public class Wild {
    static double sum(List<? extends Number> numbers) {
        double total = 0;
        for (Number n : numbers) {
            total += n.doubleValue();
        }
        return total;
    }

    public static void main(String[] args) {
        System.out.println(sum(List.of(1, 2, 3)));
        System.out.println(sum(List.of(1.5, 2.5)));
    }
}
Output

6.0
4.0

Facts

  • Generics work only with objects, not primitives: List<Integer>, not List<int>.
  • Type information is removed after compile (type erasure), so you can not do new T() or T.class.
  • Common names: T (type), E (element), K and V (key and value).
Note: A List<Integer> is not a List<Number>, even if Integer is a Number. That is why wildcards exist. Next: Java Lambda Expressions.

Congratulations! Chapter Finished. Learn more about the similar topics:
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.