Java Generics

What Are Generics?

Generics were introduced in Java 5, and they mean generalizing data types.
Generics are a way to specify in advance the data type used by a class or method.
Using generics prevents data type mismatch problems during compilation.
Also, using generics eliminates the need for type casts, improving program performance.

BadGenericsSample.java

package com.devkuma.basic.generics;

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

public class BadGenericsSample {
    public static void main(String[] args) {
        List arrayList = new ArrayList();
        arrayList.add("test");

        String test = arrayList.get(0); // error
    }
}

As in the example above, if you store data without specifying a data type with generics, the stored data is saved as the Object type. Therefore, when retrieving it, a type conversion must be performed.

GoodGenericsSample.java

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

public class GoodGenericsSample {
    public static void main(String[] args) {
        List<String> arrayList = new ArrayList();
        arrayList.add("test");

        String test = arrayList.get(0); // ok
    }
}

If you specify the data type in advance with generics, the stored data is saved as the specified type, so you do not need to perform type conversion when retrieving it.

How to Use Generics

Generics are declared as follows.

public class ClassName<T> 
public interface InterfaceName<E>

There is no fixed rule for what goes inside the <> brackets, but uppercase alphabet letters are generally used.

Using Generics in a Class

The following is an example of using generics in a class.

package com.devkuma.basic.generics.ex2;

public class GenericsSampleClass<T> {

    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

When using a class written like this, specify the data type when creating an instance.

package com.devkuma.basic.generics.ex2;

public class GenericsSample {

    public static void main(String[] args) {
        GenericsSampleClass<String> classSample = new GenericsSampleClass();
        classSample.setT("SetItem");
        String getItem = classSample.getT();
    }
}

Using Generics in an Interface

The following is an example of using generics in an interface.

package com.devkuma.basic.generics.ex3;

public interface GenericInterfaceSample<T> {
    T testMethod(T t);
}

For an interface written like this, the data type can be specified in the implementation class.

package com.devkuma.basic.generics.ex3;

public class GenericInterfaceSampleImpl implements GenericInterfaceSample<String> {

    @Override
    public String testMethod(String t) {
        return null;
    }
}

Specifying the Range of Generics

The range of data types specified with generics can also be limited using inheritance.

package com.devkuma.basic.generics.ex4;

public class GenericClassSample<T extends Number>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

For a class written like this, when specifying a data type with generics, only classes that inherit from the Number class can be specified.

package com.devkuma.basic.generics.ex4;

public class GenericSample {
    public static void main(String[] args) {
        GenericClassSample<String> classSample = new GenericClassSample(); // error
        classSample.setT("SetItem");
        String getItem = classSample.getT();
    }
}

As shown above, String is specified as the generic data type, but because String is not a subclass of the Number class, a compile error occurs.