Java中List<? extends T>与List<? super T>的区别(值得珍藏)

1. 名词解释

1.1 ?

?表示类型通配符,即具体传什么参数类型,在List定义时不用考虑。

1.2

这里的 表示泛型,T 表示泛型中装载的类型为T类型,等到需要的时候,我们可以具体这个 T。我们在使用动态数组实现 ArrayList 的时候,如果希望这个 ArrayList 不仅仅支持一个类型的话,我们可以给这个 ArrayList 定义泛型,泛型中存放的是T类型。在实际创建出这个 ArrayList 对象时,我们可以指定泛型中的具体类型。

1.3

类型上界,这里的 ? 可以是 T 类型或者 T 的子类类型。

1.4

类型下界,这里的?可以是T类型或者T的超类(父类)类型,但不代表我们可以往里面添加任意超类类型的元素。

2. 与区别

2.1 List

这个通配符表示列表中的元素类型是T或T的子类。我们可以将该列表视为只读列表,因为我们只能从中读取元素,而不能添加或修改元素。这是因为编译器无法确定实际存储的元素类型。

  • 可以安全地从该列表中读取元素并将其视为T类型或T的子类类型。
  • 不能往该列表中添加任何对象,因为编译器无法确定实际存储对象的类型。

例如:

List<? extends Number> numbers = new ArrayList<Integer>();

Number number = numbers.get(0); // 读取操作
numbers.add(new Integer(10)); // 错误:无法添加 Integer 对象
public class Main {

    static class A { }

    static class B extends A { }

    static class C extends A { }

    public static void main(String[] args) {
        List<? extends A> list = new ArrayList<>();
        list.add(new A());//编译报错
        list.add(new B());//编译报错
        list.add(new C());//编译报错
    }

}

2.2 List

这个通配符表示列表中的元素类型是T或T的父类。我们可以将该列表视为写入支持(contravariant)列表,因为我们可以向其中添加属于T或其子类的对象。但是在读取时无法确定实际存储对象类型。

  • 可以安全地向该列表中添加属于T或其子类(下界)对象。
  • 在从该列表中读取时,由于编译器无法确定实际存储对象的类型,需要进行强制类型转换。

例如:

List<? super Integer> integers = new ArrayList<Number>();

integers.add(new Integer(10)); // 添加 Integer 对象
integers.add(new Number(5.5)); // 添加 Number 对象(Number是Integer的父类)

Object object1 = integers.get(0); // 需要强制类型转换
Integer integer1 = (Integer) object1; // 强制类型转换为 Integer

Object object2 = integers.get(1); // 需要强制类型转换
Number number2 = (Number) object2; // 强制类型转换为 Number
public class Test {

    static class A { }
    
    static class B extends A { }

    static class C extends A { }

    public static void main(String[] args) {
        List<? super A> list = new ArrayList<>();
        list.add(new A());
        list.add(new B());
        list.add(new C());
    }
}

2.3 综合示例

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

public class Main {
    public static void main(String[] args) {
        // List
        List<? extends Animal> animals1 = new ArrayList<Animal>();
        // 编译器无法确定实际存储对象类型
        // 不能添加或修改元素
        animals1.add(null);
        Animal animal1 = animals1.get(0);

        // List
        List<? super Dog> animals2 = new ArrayList<Animal>();
        // 可以向列表中添加Dog及其子类对象
        animals2.add(new Dog());
        animals2.add(new Cat());
        
         Object object1 =  animals2.get(0);
         Animal animal3= (Animal)animal3;
         Object object2 =  animals2.get(1);
         Animal animal4= (Animal)animal4;  
    }
}

3. 总结

(1)List适用于读取数据,读取出来的数据全部用T类型接收。如果我们往此 list 中添加 T 类型不同的子类的话,各种子类无法相互转换,因此不能添加元素,但可接受初始赋值。

(2)List适用于添加元素,只能添加 T 类型或其子类类型。因为这些类型都能转换为T的任意超类类型(向上转型),因此我们可以对此 list 添加元素。只能用 Object 类型来接收获取到的元素,但是这些元素原本的类型会丢失。

你可能感兴趣的:(知识库,java,list,开发语言,泛型)