• Java泛型中 <?>、<? extends T>、<? super T>的区别


    Java泛型中的?指通配符,泛指所有类型。而<? extends T>指的是上界通配符,? super T指的是下界通配符。

    1 为什么要用通配符和边界?

    举个例子,我们假设有一个Cat类表示所有的猫科动物,那么显然加菲猫类GarfieldCat的一个子类。如果我们有一个猫科动物的笼子List<Cat>,我们理所当然地认为这个笼子也可以关加菲猫。但是,下面这行代码却在编译期报错了。

    List<Cat> cats = new ArrayList<Garfield>();
    

    所以,我们可以知道,就算容器中存放的对象类型有继承关系,容器间却是没有继承关系的。为了让容器间也存在类似的继承关系,通配符边界就应运而生了。

    2 什么是上界?

    List<? extends T>表示这个List对象可以存放TT的一切子类对象,由此,编译器认为List<? extends T>List<T>的父类,所以我们按照如下方式赋值是不会报错的。

    List<? extends Cat> cats = new ArrayList<Garfield>();
    

    然而,使用<?extends Cat>定义的List是不可以再存放任何元素的,因为编译器仅仅知道你集合中对象的类型都是?,具体是啥类型并不知道,所以肯定不能让你乱放元素,万一你放了个Object对象,那集合中的对象类型就乱套了,不再是CatCat的子类。

    使用<?extends Cat>定义的Listget()方法正常,因为取出来的元素都可以向上转型成Cat对象。

    3 什么是下界?

    List<? super T>表示这个List对象可以存放TT的一切父类对象。但是,List<? super Cat>List<Cat>的父类,但是却不是List<Garfield>的父类。也就是说,下面这行代码编译期就会报错。

    List<? super Cat> cats = new ArrayList<Garfield>();
    

    但是这样赋值就不会报错:

    List<? super Cat> cats = new ArrayList<Object>();
    

    使用<? super Cat>定义的Listget()方法会部分失效,因为所有取出来的对象都会转型为Object对象,从而丢失对象原来的信息。set()方法正常。

    import java.util.ArrayList;
    import java.util.List;
    
    public class AnimalCatGarfield {
        public static void main(String[] args) {
            List<Animal> animals = new ArrayList<>();
            List<Cat> cats = new ArrayList<>();
            List<Garfield> garfields = new ArrayList<>();
    
            animals.add(new Animal());
            cats.add(new Cat());
            garfields.add(new Garfield());
    
            // 编译出错,extendsCatFromAnimal只能赋值Cat或Cat子类的集合
            List<? extends Cat> extendsCatFromAnimal = animals;
            // 编译成功
            List<? super Cat> superCatFromAnimal = animals;
    
            // 下面两行均编译成功
            List<? extends Cat> extendsCatFromCat = cats;
            List<? super Cat> superCatFromCat = cats;
    
            // 编译成功
            List<? extends Cat> extendsCatFromGarfield = garfields;
            // 编译出错,superCatFromGarfield只能赋值Cat或Cat的父类集合
            List<? super Cat> superCatFromGarfield = garfields;
    
            // 测试add方法
            // 下面三行均编译失败,除了null以外,任何元素都不能被添加进<? extends T>集合内
            extendsCatFromCat.add(new Animal());
            extendsCatFromCat.add(new Cat());
            extendsCatFromCat.add(new Garfield());
    
            // 编译失败,只能添加Cat或Cat子类的集合
            superCatFromCat.add(new Animal());
            // 下面两行均编译成功
            superCatFromCat.add(new Cat());
            superCatFromCat.add(new Garfield());
    
            // 测试get方法
            // 所有super操作都能够返回元素,但是泛型丢失,只能返回Object对象
    
    
            // 以下extends操作能够返回元素
            Object catExtends2 = extendsCatFromCat.get(0);
            Cat catExtends1c = extendsCatFromCat.get(0);
            // 编译出错,虽然Cat集合从Garfield赋值而来,但是类型擦除后,是不知道的
            Garfield garfield1 = extendsCatFromGarfield.get(0);
    
        }
    }
    
  • 相关阅读:
    JVM学习-垃圾回收算法
    JVM学习-jvm判断对象已死的方法
    JVM学习-jvm内存区域
    python 多线程
    Python+unittest+requests+excel实现接口自动化测试框架
    linux 运行tensorflow文件缺少_bz2问题及解决
    获取url地址
    微信小程序的小问题(2)
    微信小程序的小问题(1)
    前端知识
  • 原文地址:https://www.cnblogs.com/muuu520/p/14437983.html
Copyright © 2020-2023  润新知