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);

    }
}
原文地址:https://www.cnblogs.com/muuu520/p/14437983.html