Java Generic Programming

In this post, I will introduce generic programming in Java and talk about how to define and use generic class, generic method, generic interface and generic wildcard.

Generic type is an unknown data type. For examples, ‘E’ represents elements and ‘T’ represents types. When creating the collection object, we need to specify the specific date type.


Difference between using and not using generic type

// If we do not use generic type
// The default type will be 'Object', so it can store any type of data
private static void demo01() {
    ArrayList list = new ArrayList();
    list.add("abc");
    list.add(1);
    Iterator it = list.iterator();
    while (it.hasNext()) {
        Object obj = it.next();
        System.out.println(obj);
        // If we want to get the length of strings
        // ClassCastException (Dangerous)
        // String s = (String) obj;
        // System.out.println(s.length());
    }
}

// If we use generic type
// Error checking when compiling
private static void demo02() {
    ArrayList<String> list = new ArrayList<>();
    list.add("abc");
    list.add("111");
    Iterator<String> it = list.iterator();
    while (it.hasNext()) {
        String s = it.next();
        System.out.println(s);
        System.out.println(s.length());
    }
}

Define and use generic class

// Define the class
public class GenericClass<E> {
    private E name;

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }
}

// Use the class
public class Demo {
    public static void main(String[] args) {
        GenericClass<Integer> gc = new GenericClass<>();
        gc.setName(100);
        System.out.println(gc.getName());
    }
}

Define and use generic method

// Define the method
public class GenericMethod {
    // Member method
    public <M> void method01(M m) {
        System.out.println(m);
    }

    // Static method
    public static <S> void method02(S s) {
        System.out.println(s);
    }
}

// Use the method
public class Demo03 {
    public static void main(String[] args) {
        GenericMethod gm = new GenericMethod();
        gm.method01(111);
        gm.method01("abc");

        GenericMethod.method02(222);
        GenericMethod.method02("Using generic static method");
    }
}

Define and use generic interface

// Define the interface
public interface GenericInterface<I> {
    public abstract void method(I i);
}

// Two ways of defining the implementing class
// First approach
public class GenericInterfaceImpl1 implements GenericInterface<String> {
    @Override
    public void method(String s) {
        System.out.println(s);
    }
}

// Second approach
public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
    @Override
    public void method(I i) {
        System.out.println(i);
    }
}

// Using interface
public class Demo {
    public static void main(String[] args) {
        // First approach
        GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
        gi1.method("abc");

        // Second approach
        GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<Integer>();
        gi2.method(123);
    }
}

Define and use generic wildcard

When doing generic programming, if we are unsure about the type of received data, we can use the wildcard <?> to represent the type of data we will receive.

public class Demo05 {
    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(2);

        ArrayList<String> list02 = new ArrayList<>();
        list02.add("aa");
        list02.add("bb");

        printArray(list01);
        printArray(list02);
    }

    // Define a method to traverse all types of ArrayList
    public static void printArray(ArrayList<?> list) {
        Iterator<?> it = list.iterator();
        while (it.hasNext()) {
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}

We cannot use <?> to define collections because it can only be used for receiving unknown parameters.

ArrayList<?> list03 = new ArrayList<>(); // Incorrect code

Advanced operation of generic wildcard

public class Demo06 {
    public static void main(String[] args) {
        Collection<Integer> list1 = new ArrayList<Integer>();
        Collection<String> list2 = new ArrayList<String>();
        Collection<Number> list3 = new ArrayList<Number>();
        Collection<Object> list4 = new ArrayList<Object>();
        
        // Integer extends Number extents Object
        // String extends Object
        
        getElement1(list1);
        getElement1(list2); // Wrong
        getElement1(list3);
        getElement1(list4); // Wrong
        
        getElement2(list1); // Wrong
        getElement2(list2); // Wrong
        getElement2(list3);
        getElement2(list4);
    }
    
    // We can only receive 'Number' or its derived class
    public static void getElement1(Collection<? extends Number> coll) {}
    
    // We can only receive 'Number' or its base class
    public static void getElement2(Collection<? super Number> coll) {}
}