Java Language Creating a Generic Class

Download Java Language for free

Example

Generics enable classes, interfaces, and methods to take other classes and interfaces as type parameters.

This example uses generic class Param to take a single type parameter T, delimited by angle brackets (<>):

public class Param<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

To instantiate this class, provide a type argument in place of T. For example, Integer:

Param<Integer> integerParam = new Param<Integer>();

The type argument can be any reference type, including arrays and other generic types:

Param<String[]> stringArrayParam;
Param<int[][]> int2dArrayParam;
Param<Param<Object>> objectNestedParam;

In Java SE 7 and later, the type argument can be replaced with an empty set of type arguments (<>) called the diamond:

Java SE 7
Param<Integer> integerParam = new Param<>();

Unlike other identifiers, type parameters have no naming constraints. However their names are commonly the first letter of their purpose in upper case. (This is true even throughout the official JavaDocs.)
Examples include T for "type", E for "element" and K/V for "key"/"value".


Extending a generic class

public abstract class AbstractParam<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

AbstractParam is an abstract class declared with a type parameter of T. When extending this class, that type parameter can be replaced by a type argument written inside <>, or the type parameter can remain unchanged. In the first and second examples below, String and Integer replace the type parameter. In the third example, the type parameter remains unchanged. The fourth example doesn't use generics at all, so it's similar to if the class had an Object parameter. The compiler will warn about AbstractParam being a raw type, but it will compile the ObjectParam class. The fifth example has 2 type parameters (see "multiple type parameters" below), choosing the second parameter as the type parameter passed to the superclass.

public class Email extends AbstractParam<String> {
    // ...
}

public class Age extends AbstractParam<Integer> {
    // ...
}

public class Height<T> extends AbstractParam<T> {
    // ...
}

public class ObjectParam extends AbstractParam {
    // ...
}

public class MultiParam<T, E> extends AbstractParam<E> {
    // ...
}

The following is the usage:

Email email = new Email();
email.setValue("test@example.com");
String retrievedEmail = email.getValue();

Age age = new Age();
age.setValue(25);
Integer retrievedAge = age.getValue();
int autounboxedAge = age.getValue();

Height<Integer> heightInInt = new Height<>();
heightInInt.setValue(125);

Height<Float> heightInFloat = new Height<>();
heightInFloat.setValue(120.3f);

MultiParam<String, Double> multiParam = new MultiParam<>();
multiParam.setValue(3.3);

Notice that in the Email class, the T getValue() method acts as if it had a signature of String getValue(), and the void setValue(T) method acts as if it was declared void setValue(String).

It is also possible to instantiate with anonymous inner class with an empty curly braces ({}):

AbstractParam<Double> height = new AbstractParam<Double>(){};
height.setValue(198.6);

Note that using the diamond with anonymous inner classes is not allowed.


Multiple type parameters

Java provides the ability to use more than one type parameter in a generic class or interface. Multiple type parameters can be used in a class or interface by placing a comma-separated list of types between the angle brackets. Example:

public class MultiGenericParam<T, S> {
    private T firstParam;
    private S secondParam;
   
    public MultiGenericParam(T firstParam, S secondParam) {
        this.firstParam = firstParam;
        this.secondParam = secondParam;
    }
    
    public T getFirstParam() {
        return firstParam;
    }
    
    public void setFirstParam(T firstParam) {
        this.firstParam = firstParam;
    }
    
    public S getSecondParam() {
        return secondParam;
    }
    
    public void setSecondParam(S secondParam) {
        this.secondParam = secondParam;
    }
}

The usage can be done as below:

MultiGenericParam<String, String> aParam = new MultiGenericParam<String, String>("value1", "value2");
MultiGenericParam<Integer, Double> dayOfWeekDegrees = new MultiGenericParam<Integer, Double>(1, 2.6);
Deciding between `T`, `? super T`, and `? extends T`