Upper and lower bounds of java generics

1.Upper bound

The upper bound of Java generics is used to restrict the generic type parameter to a subtype of a certain type, usually extendsexpressed using the keyword. Here is a sample code where Tthe upper bound of is Comparable<T>, indicating Tthat must be a type that implements Comparable<T>the interface:

public class Example<T extends Comparable<T>> {
    private T[] array;

    public Example(T[] array) {
        this.array = array;
    }

    public T getMax() {
        T max = array[0];
        for (T item : array) {
            if (item.compareTo(max) > 0) {
                max = item;
            }
        }
        return max;
    }
}

In the above code, Examplethe class uses generic type parameters Tand extendsspecifies Tthe upper bound of through the keyword Comparable<T>, indicating that Tit must be a type that implements Comparable<T>the interface. In the method, a method of the type getMaxis used , which is a method defined in the interface.TcompareToComparable<T>

By using the upper bound of generics, we can check whether the types of generic type parameters meet the requirements at compile time, thus improving the type safety and readability of the code. At the same time, you can also directly call the methods and properties defined in its upper bound type when using generic type parameters.

In Java generics, you can also use multiple upper bounds, indicating that generic type parameters must be subtypes of multiple types at the same time. This feature is called multiple upper bounds (Multiple Bounds). Multiple upper bounds use `&` to connect multiple types, where the first type must be a class or interface, and the other types must be interfaces. For example:


public class Example<T extends Comparable<T> & Serializable> {
    //
}

In the above code, the upper bound of `T` is a type that implements both `Comparable<T>` and `Serializable` interfaces.

It should be noted that the upper and lower bounds in Java generics are one-way limited, that is, only the upper or lower bounds of generic type parameters can be limited, but not both at the same time. That is, we cannot use the `extends` and `super` keywords at the same time to qualify a generic type parameter.

The following is an example, which defines a `print` method and uses the upper bound wildcard `<? extends Number>` to limit the parameter type:

public class Example {
    public static void print(List<? extends Number> list) {
        for (Number n : list) {
            System.out.print(n + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3);
        List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
        print(intList);     // 输出:1 2 3
        print(doubleList);  // 输出:1.1 2.2 3.3
    }
}

In the above code, the parameter type of the `print` method is `List<? extends Number>`, which means that the element type in the list must be the `Number` type or its subtype. This way, we can pass `List<Integer>` and `List<Double>` as parameters to the method since they are both subtypes of `Number`.

2. Lower bound

The lower bound of Java generics is used to restrict the generic type parameter to the parent type of a certain type, usually expressed using the `super` keyword.

The following is an example code, where the lower bound of `T` is `Number`, which means that `T` must be of type `Number` or its supertype:

public class Example<T super Number> {
    private T value;

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

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

    public T getValue() {
        return value;
    }
}

In the above code, the `Example` class uses the generic type parameter `T`, and specifies that the lower bound of `T` is `Number` through the `super` keyword, which means that `T` must be of type `Number` or Its parent type. In the `setValue` method, any value of type `Number` can be assigned to the `value` member variable, including `Number`, `Integer`, `Double` and so on.

By using the lower bound of generics, we can check whether the types of generic type parameters meet the requirements at compile time, thus improving the type safety and readability of the code. At the same time, you can also directly use the methods and properties of its lower bound type when using generic type parameters.

Like upper bounds, lower bounds can also be used with wildcards, for example:

public static void addToList(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

public static void main(String[] args) {
    List<Number> numberList = new ArrayList<>();
    addToList(numberList);
    System.out.println(numberList); // 输出:[1, 2, 3]

    List<Object> objectList = new ArrayList<>();
    addToList(objectList);
    System.out.println(objectList); // 输出:[1, 2, 3]
}

In the above code, the parameter type of the `addToList` method is `List<? super Integer>`, which means that the element type in the list must be the parent type of `Integer`. In the method we can add a value of type `Integer` to this list. In the `main` method, we first create a list with element type `Number` and a list with element type `Object`, and then pass them as parameters to the `addToList` method and output them. As you can see, the final two lists contain the elements `1`, `2`, and `3`, indicating that using the lower bound of generics can make the method more flexible and can receive more types of lists as parameters.

Guess you like

Origin blog.csdn.net/qq_55888300/article/details/130520609