JDK8“? extends T”和“? super T”泛型通配符

在 Java 8(及其后续版本)的泛型中,? extends T 和 ? super T 是两种通配符,它们用于表示泛型的上界和下界。这两个通配符的主要目的是增加代码的灵活性和复用性。

在 Java 8(及其后续版本)的泛型中,? extends T 和 ? super T 是两种通配符,它们用于表示泛型的上界和下界。这两个通配符的主要目的是增加代码的灵活性和复用性。

? extends T(上界通配符)

这个通配符表示一个未知的类型,它是类型 T 或 T 的任何子类型。当你需要从泛型集合中读取数据,但不需要写入数据时,这个通配符很有用。例如:

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

public class Demo20240723162040 {

    public static void main(String[] args) {
        List<? extends Number> list = getData();
        for(Number n : list) {
            // 强制类型转换,可能会出现 ClassCastException 异常
            // 因为 Number 的子类不仅仅有 Integer,还有 Double 等等
            Integer i = (Integer) n;
            System.out.println("n = " + n + ", i = " + i);
        }

        // 编译错误
        // 因为当前 list 中的具体类型不知道,可能是 Integer 也可能是 Double
        // 因此,不允许向 list 中添加元素
        //list.add(Integer.valueOf(10));
    }

    private static List<? extends Number> getData() {
        List<Integer> data = new ArrayList<>();
        data.add(1);
        data.add(2);
        data.add(3);
        return data;
    }

}

在这个例子中,list 可以是 List<Integer>、List<Double> 或任何其他 Number 的子类型的列表。但是,你不能向这样的列表添加元素,因为编译器不知道列表的确切类型。

Number 类的直接子类如下图:

5e012ad0f7f5310e461ec4815508738e_1721722774881-88b1e071-f52e-4e70-bd2b-11fc9c00d00c_x-oss-process=image%2Fformat%2Cwebp.png

? super T(下界通配符)

这个通配符表示一个未知的类型,它是类型 T 或 T 的任何父类型(包括 Object)。当你需要向泛型集合中写入数据,但不需要读取数据时,这个通配符很有用。

示例:

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

public class Demo20240723162951 {

    public static void main(String[] args) {
        List<? super Integer> list = getData();
        list.add(4); // OK
        System.out.println(Arrays.toString(list.toArray()));
        //[1, 2, 3, 4]

        // 错误:编译器不知道 list 中存储的是什么类型,可能是 Integer,也可能是 Number
        // list 里面存储的
        //Integer value1 = list.get(0);
        //Number value2 = list.get(0);

        // 如果我们明确知道类型,可以进行强制类型转换,或者探测
        Object value = list.get(0);
        if(value instanceof Integer) {
            Integer value1 = (Integer)value;
            System.out.println(value1); //1
        } else if(value instanceof Number) {
            Number value2 = (Number)value;
            System.out.println(value2);
        }
    }

    private static List<? super Integer> getData() {
        List<Integer> data = new ArrayList<>();
        data.add(1);
        data.add(2);
        data.add(3);
        return data;
    }

}

在这个例子中,list 可以是 List<Number>、List<Object> 或任何其他 Integer 的父类型的列表。你可以向这样的列表添加 Integer 或其任何子类型的对象。但是,你不能从这样的列表读取元素到一个具体的类型,因为编译器不知道列表的确切类型。 IDEA 会给出如下错误信息:

9051fad82f0ba2fce7a3fc53f84dfb47_1721723606449-bc89eee6-a1b2-4542-b36e-e67a69ffa6db_x-oss-process=image%2Fformat%2Cwebp.png

总结

? extends T 允许你读取数据,但不允许你写入数据。因为它是协变(Covariance)的,如果对于类型 A 和 B,A 是 B 的子类型,并且 F[A] 是 F[B] 的子类型,那么 F 被称为是协变的。

? super T 允许你写入数据,但不允许你安全地读取数据,因为它是逆变(Contravariance)的,如果对于类型 A 和 B,A 是 B 的子类型,并且 F[B] 是 F[A] 的子类型,那么 F 被称为是逆变的。

业精于勤,荒于嬉。——韩愈《进学解》
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号