wisconsin
Generics

Generics

Curated by Last modified: 4 min read

Generics are a way to make classes and methods more flexible and reusable. They allow you to define a class or method with a placeholder for the type of data it will work with. This allows you to write code that can work with different types of data without having to rewrite it for each type.

In Java, they are defined using angle brackets (<T>) to specify a type parameter.

By default, the only methods that can be called on a generic type are those that are available to all objects (e.g., toString(), equals(), etc.). If you want to call a method that is specific to the type parameter, you need to use a bounded type parameter.


## Generic Methods

Generic methods are methods that can work with different types of data. For example, the following method takes an array of any type and prints each element:

```java
public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

You can call this method with an array of any type, and it will print each element:

String[] stringArray = {"Hello", "World"};
Integer[] intArray = {1, 2, 3};

printArray(stringArray);
printArray(intArray);

Generic Classes

Classes can also be generic, allowing you to create a class that can work with different types of data.

public class Box<T> {
    private T value;

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

    public T getValue() {
        return value;
    }
}

You can create an instance of a generic class by specifying the type parameter:

Box<String> stringBox = new Box<>("Hello");
Box<Integer> intBox = new Box<>(42);

Generic Interfaces

Interfaces can also be generic, allowing you to define interfaces with type parameters.

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

Classes that implement a generic interface must specify the type parameters:

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;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

Bounded Type Parameters

You can restrict the types that can be used with a generic class or method by using bounded type parameters with the extends keyword. This allows you to specify that the type parameter must be a class, a subclass of a certain class, or implement a certain interface.

For example, the following method only accepts types that implement the Comparable interface:

public <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

This method can be called with any type that implements the Comparable interface:

Integer maxInt = max(5, 10);
String maxString = max("apple", "banana");

Wildcards

Wildcards (?) can be used to represent an unknown type in a generic class or method. They allow you to write code that can work with any type without specifying the exact type.

For example, the following method accepts a list of any type and prints each element:

public void printList(List<?> list) {
    for (Object element : list) {
        System.out.println(element);
    }
}

Bounded Wildcards

You can also use bounded wildcards to restrict the types that can be used with a wildcard (higher bound). For example, the following method only accepts lists of types that extend Number:

public void printNumbers(List<? extends Number> numbers) {
    for (Number number : numbers) {
        System.out.println(number);
    }
}

This method can be called with a list of any type that extends Number, such as Integer, Double, or Float.

Another example is the super keyword, which restricts the types to be superclasses of a certain type (lower bound).

public void addNumbers(List<? super Integer> numbers) {
    numbers.add(1);
    numbers.add(2);
}

Source

The existing course note was retained and normalized during the merge.

Related: Topic index.

Linked to this page