Generic - HolmesJJ/OOP-FP GitHub Wiki

Usage of Generic

There are three ways to use generics: Generic Class, Generic Interface, Generic Method

Generic Class

Generic type used in the definition of class is called Generic Class, such as: List, Set, Map, etc.

Note

  • Generic type parameter can be any letter between A-Z, such as T, E, K, V, etc.

Template

class [class name] <[Generic type parameter: any letter between A-Z]> {
    private [Generic type parameter: any letter between A-Z] [variable name]; 
    ....
}

A normal Generic Class

// When instantiating a generic class, the specific type of T must be specified
public class Generic<T> { 
    // The type of the key variable is T, and T is specified externally
    private final T key;

    // The type of the parameter key of the generic constructor is T, and T is specified externally
    public Generic(T key) { 
        this.key = key;
    }

    // The return value type of the generic method is T, and T is specified externally
    public T getKey() { 
        return key;
    }
}

public static void genericExample() {
    // The type of the argument passed in must be the same as the generic type parameter, which is Integer.
    Generic<Integer> genericInteger = new Generic<Integer>(123);
    // The type of the argument passed in must be the same as the generic type parameter, which is String.
    Generic<String> genericString = new Generic<String>("Hello World");
    
    System.out.println("key is " + genericInteger.getKey());
    System.out.println("key is " + genericString.getKey());
}

/* Output */
key is 123
key is Hello World

Note

  • When using generics, if the generic type parameter is defined, the restriction of generic will take effect. Otherwise, the type of the Generic Method or variable in the Generic Class can be defined as any type.

Example

public static void genericExample2() {
    Generic generic1 = new Generic("Hello World");
    Generic generic2 = new Generic(123);
    Generic generic3 = new Generic(123.45);
    Generic generic4 = new Generic(true);

    System.out.println("key is " + generic1.getKey());
    System.out.println("key is " + generic2.getKey());
    System.out.println("key is " + generic3.getKey());
    System.out.println("key is " + generic4.getKey());
}

/* Output */
key is Hello World
key is 123
key is 123.45
key is true

Note

  • Generic type parameter can only be reference type, not primitive type.
  • Cannot use the instanceof operation on a generic class defined by the certain type.
// Error
if (genericInteger instanceof Generic<Integer>) {
}

Generic Interface

The definition and usage of Generic Interface and Generic Class are basically the same.

A normal Generic Interface

public interface IGeneric<T> {
    public T next();
}

When the class that implements the Generic Interface does not define the generic type parameter

/**
 * The generic declaration needs to be added to the class if the generic type parameter is not defined, 
 * which is class FruitGeneric<T> implements IGeneric<T> {.
 * If the generic is not declared, such as:class FruitGeneric<T> implements IGeneric<T>,
 * compilation error will be thrown:"Unknown class".
 */
class FruitGeneric<T> implements IGeneric<T> {
    @Override
    public T next() {
        return null;
    }
}

When the class that implements the Generic Interface defines the generic type parameter, such as String

/**
 * Only one Generic Interface IGeneric<T> is created, 
 * but the generic type parameter T can be defined as different types 
 * to form different types of Generator interface.
 * Note: all the T in IGeneric<T>, such as IGeneric<T>,public T next(); must be replaced with String.
 */
public class FruitGeneric implements IGeneric<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

Generic Method

Generic Class VS Generic Method

  • Generic Class: Specify the specific type of generic when instantiating the class.
  • Generic Method: Specify the specific type of generic when calling the method.

Note

  • The between public and the return type T can be regarded as declaring this method as a Generic Method.
  • Only method that declare is Generic Method, otherwise, it is not a Generic Method.
  • indicates that the method will use the generic type T, then the generic type T can be used in the method.
  • Generic type parameter can be any letter between A-Z, such as T, E, K, V, etc.
  • If the generic type parameter in the Generic Method and the Generic Class are the same letter, such as T, these two Ts are different.

A normal Generic Method

public <T> T getKey(Generic<T> generic) {
    // return type is T, which is the same as the generic type parameter of Generic<T>
    return generic.getKey();
}

Basic usage of generic methods

// This is a Generic Class as mentioned above
public class Generic<T> { 

    private final T key;

    public Generic(T key) { 
        this.key = key;
    }

    /**
     * Generic is used in the method, but this is not a Generic Method.
     * This is just a normal method, but its return type is T, which the Generic Class has already declared.
     * Therefore, the generic type parameter T can still be used in this method.
     */
    public T getKey() { 
        return key;
    }

    /**
     * This method will cause a compilation error "cannot resolve symbol E".
     * The generic type parameter E is not declared in the class declaration as well as the method declaration,
     * the compiler will not recognize it when E is used as the generic type parameter and return type.
     */
    public E setKey1(E key) {
        this.key = key;
        return this.key;
    }

    // This is not a Generic Method, just uses the T as the type of the generic parameter.
    public void showKeyValue1(T key) {
        System.out.println("key value is " + key);
    }

    // This is not a Generic Method, just uses the Generic<T> as the type of the parameter g.
    public void showKeyValue2(Generic<T> g) {
        System.out.println("key value is " + g.getKey());
    }

    // This is not a Generic Method, just uses the Generic<?> as the type of the parameter g.
    public void showKeyValue3(Generic<?> g) {
        System.out.println("key value is " + g.getKey());
    }

    /**
     * This method will cause a compilation error "cannot resolve symbol E".
     * Only the generic type parameter T is declared in the method declaration,
     * but E is not declared in the class declaration as well as the method declaration,
     * the compiler cannot recognize it when E is used as the generic type parameter.
     */
    public <T> T showKeyValue4(Generic<E> g) {
        ...
    }

    /** 
     * This is a Generic Method.
     * The <T> between public and the return type T is necessariy,
     * which indicates that this is a Generic Method and declares a generic type parameter T.
     *
     * Note: The generic type parameter T in this Method is different from the generic type 
     *       parameter T in the Generic Class.
     *       In order to distinguish them more easily, it is better to use different letters.
     *
     * The number of generic type parameters can also be multiple, such as:
     * public <T,K> K showKeyValue6(Generic<T> g) {
     *     ...
     * }
     */
    public <T> T showKeyValue5(Generic<T> g) {
        System.out.println("key value is " + g.getKey());
        T key = g.getKey();
        return key;
    }
}

Static method and Generic

  • If the static method uses generic, the static method must also be defined as a Generic Method.
public class StaticGeneric<T> {
    /**
     * If you define a static method using generic, you need to add an additional generic declaration 
     * to define this method as a generic method
     * Static methods cannot use the generic type parameter that has been declared in a Generic Class such as T
     * For example: public static void show(T t) { ... }, compilation error will be thrown:
     *              "StaticGeneric cannot be refrenced from static context"
     */
    public static <T> void show(T t) {
        ...
    }
}

Generic Method and Variable Arguments

public static <T> void printMsg(T... args) {
    for(T t : args){
        System.out.println("t is " + t);
    }
}

printMsg("Hello World", 123, 123.45, false);

/* Output */
t is Hello World
t is 123
t is 123.45
t is false

Generic Wildcard

Generic Type Parameter and Generic Wildcard
When using generic, we can also limit the upper and lower boundaries of the generic formal parameters. For example, the generic formal parameters are only allowed to pass in a certain type of superclass or a certain type of subclass.

Define an upper or lower boundary for Normal Method

The formal parameters passed in must be a subclass of the specified class

public static void showValue(Generic<? extends Number> obj) {
    System.out.println("key is " + obj.getKey());
}

public static void main(String[] args) {
    Generic<String> generic1 = new Generic<String>("11111");
    Generic<Integer> generic2 = new Generic<Integer>(2222);
    Generic<Float> generic3 = new Generic<Float>(2.4f);
    Generic<Double> generic4 = new Generic<Double>(2.56);

    // The compiler will throw an error for this line of code
    // because String is not a subclass of Number
    // showValue(generic1);
    showValue(generic2);
    showValue(generic3);
    showValue(generic4);
}

Define an upper or lower boundary for Generic Class

The formal parameters passed in must be a subclass of the specified class

// This is a Generic Class as mentioned above
public class Generic<T extends Number> {
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }
}

Define an upper or lower boundary for Generic Method

The formal parameters passed in must be a subclass of the specified class
Must be defined on the between public and the return type

/**
 * public <T> T showKeyName(Generic<T extends Number> container),
 * the compiler will throw an error:"Unexpected bound"
 */
public <T extends Number> T showKey(Generic<T> container) {
    System.out.println("container key: " + container.getKey());
    T test = container.getKey();
    return test;
}

public static void main(String[] args) {
    // The compiler will throw an error for this line of code
    // because String is not a subclass of Number
    // Generic<String> generic1 = new Generic<String>("11111");
}

Generic Array

Java does not support Generic Array. The following code will throw an error

List<String>[] ls = new ArrayList<String>[10];

but the following code can work

List<String>[] ls = new ArrayList[10]
⚠️ **GitHub.com Fallback** ⚠️