关于java泛型中List<? extends Base>的疑问?

作者站长头像
站长
· 阅读数 26

现在有代码如下,Sub是Base的子类:

public class Base {

}

public class Sub extends Base{
}

我能理解下面的代码:

    // 相当于list2的泛型是Base或者Base的超类,
    // 所以,list2中可以放Base或者Base的子类,因为list2的泛型如果是Base,那显然可以添加Base的实例;
    // Base的泛型如果是其超类,那显然,Base的任意子类都能向上转型为Base的超类
    List<? super Base> list1 = new ArrayList<>();
    list1.add(new Sub());
    list1.add(new Base());

现在的问题是,为什么不能调用list2的add方法添加,即便是添加Base实例也不行?

List<? extends Base> list2 = new ArrayList<>();

如果按照上面的理解,list2的泛型可以是Base或者Base的子类,如果说子类之间不能互相转型,那么,至少可以添加Base的实例吧?或者说,添加Base的子类也能转为Base类型,为什么java不允许这么做呢?

回复
1个回答
avatar
test
2024-07-01

因为 List<Base> 表示 里面是 Base 类型的对象但是 List<? extends Base> 表示 这是一个 List<Base> 或者 List<Sub>因此无法判断到底能放进去什么

这里有一个比较全的例子

class Scratch {
    public static void main(String[] args) {

        // 类型参数是一个确定的类型的时候,读写操作都可以执行
        Bag<Fruit> fruitBag = new Bag<>();
        fruitBag.set(new Apple());
        fruitBag.set(new Pear());
        Fruit fruit = fruitBag.get();

        Bag<Apple> appleBag = new Bag<>();
        Bag<Pear> pearBag = new Bag<>();

        // 这里会报错,因为 Bag<Fruit> 和 Bat<Apple> Bag<Pear> 类型不兼容
        Bag<Fruit> badBag1 = appleBag;
        Bag<Fruit> badBag2 = pearBag;

        // 这里可以通过,因为 Bag<? extend Fruit> 和 Bag<Apple> 和 Bag<Pear> 类型兼容
        Bag<? extends Fruit> appleFruitBag = appleBag;
        Bag<? extends Fruit> pearFruitBag = pearBag;

        // extends 的情况,只能'读'不能'写'
        // 因为 Bag<? extend Fruit> 可能是 Bag<Apple> 也可能是<Pear>
        // 所以无法确定能塞进去什么
        appleFruitBag.set(new Apple());
        appleFruitBag.set(new Pear());
        pearFruitBag.set(new Apple());
        pearFruitBag.set(new Pear());
        // 但是无论是什么,都是 Fruit,所以可以读取出来
        Fruit fruit1 = appleFruitBag.get();
        Fruit fruit2 = pearFruitBag.get();

        // super 的情况,只能'写'不能'读'(只能读出 Object)
        Bag<Object> objectBag = new Bag<>();
        Bag<? super Apple> superAppleBag1 = fruitBag;
        Bag<? super Apple> superAppleBag2 = objectBag;
        Bag<? super Apple> superAppleBag3 = appleBag;
        // 因为类型范围都比 Apple 大,所以可以放 Apple 进去
        superAppleBag1.set(new Apple());
        superAppleBag2.set(new Apple());

        // 因为不知道具体的类型是什么,因此也不能放父类对象进去
        superAppleBag2.set(new Fruit());

        // 同样因为不知道具体类型是什么,只能读 Object 类型出来
        Object o = superAppleBag2.get();
    }

    public static class Bag<T> {
        public void set(T t) {
        }

        public T get() {
            return null;
        }
    }

    public static class Fruit {
    }

    public static class Apple extends Fruit {
    }

    public static class Pear extends Fruit {
    }
}
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容