java泛型extends和super详解

半兽人 发表于: 2016-06-29   最后更新时间: 2022-04-11 18:56:02  
{{totalSubscript}} 订阅, 7,165 游览

首先,Java有泛型这一个概念,初衷是为了保证在运行时出现的错误能提早放到编译时检查。

基于这个前提,我们来看看以下的问题:

// 编译会报错
// List <? extends Fruit> appList2 = new ArrayList();
// appList2.add(new Fruit());
// appList2.add(new Apple());
// appList2.add(new RedApple());

List <? super Fruit> appList = new ArrayList();
appList.add(new Fruit());
appList.add(new Apple());
appList.add(new RedApple());

假设现在有这么一个类的继承树,Plant -> Fruit -> Apple -> RedApple

List<? extends Fruit> appList2的意思,是一个列表,这个列表里面的元素是Fruit的某个子类T,那么在从appList2中取出一个元素时,编译器会自动把这个元素转型为T。那么现在假设T是RedApple,很显然你往这样一个appList2中add一个Apple类型的元素,取出后转型为RedApple必然会失败;同样的情况如果T是Apple,你add一个Fruit类型的元素,取出后转型为Apple,也会抛出异常。也就是说,编译器会把列表中取出的元素转型为某种类型,但编译器不确定这种转型是不是会成功,即在不保证运行时能顺利进行,因此就不允许你add任何类型的元素。

再来看看List<? super Fruit> appList,这个列表的元素都是Fruit的父类T,也就是说当你从这个列表中get一个元素时,编译器会自动加一句转型为T的代码。好,现在你往appList中add一个Apple类型的元素,取出时转型为T,由于T是Apple的父类,向上转型没有问题;加一个RedApple类型的元素,也一样不会有问题。也就是说,只要保证你往appList中添加的元素是Fruit的子类,编译器就可以保证在转型为T时不会抛出异常。因此第二种写法可以过编译。

List :”存的时候只能选一个类型。“
List<? extends Fruit> 意思: List中所有元素都是Fruit的子类(包含本身),
List<? super Fruit> 意思: List中所有元素都是Fruit的父类(包含本身)

1、List <? extends Fruit>

假设:Fruit有子类A、B、C 那么 list.add(A);list.add(B);list.add(C);显然错误(不能存多个类)。

虽然我们现在看的是ABC3个类就会问为什么会把不同类型的存进去,我这样存不就好了。list.add(A); list.add(A); 其实这也是错误的,因为在运行之前他可不知道你到底add进去的东西是什么类型,是一样还是不一样,因实例化的时候是 ? 待定。为了避免类型不同的情况,所以会编译不通过。

2、List <? super Fruit>

假设:Fruit有子类A、B、C 那么 list.add(A); list.add(B); list.add(C); 这却是可以的,为什么呢:

因为他是这么存的:list.add((Fruit)A); list.add((Fruit)B); 自动强转了。因为小转大是隐性的,大转小才是强转需要加类型。

那这里为什么又不能存Fruit的父类呢? 因为 见假设1,它是?号,类型代表待定,不跑起来他也不知道你到底存的什么。所以我们能手动add()进去的数据都必须是绝对安全的(最低级父类:本身)才能通过。所以直接add父类也是不行的。

来个复杂的例子

<T extends Comparable<? super T>>

首先这是运用了java的泛型

1、extends后面跟的类型如<任意字符 extends 类/接口>表示泛型的上限

import java.util.*;
class Demo<T extends AbstractList>{}
public class Test
{
    public static void main(String[] args) {
    Demo<ArrayList> p = null; // 编译正确
    //这里因为ArrayList是AbstractList的子类所以通过
    //如果改为Demo<AbstractCollection> p = null;就会报错这样就限制了上限
    }
}

2、同样的super表示泛型的下限
3、<T extends Comparable<? super T>>这里来分析T表示任意字符名,extends对泛型上限进行了限制即T必须是Comparable<? super T>的子类,然后<? super T>表示Comparable<>中的类型下限为T!这样来看一段代码辅助理解:

import java.util.GregorianCalendar;

class Demo<T extends Comparable<? super T>>{}

public class Test1
{
    public static void main(String[] args) {
       Demo<GregorianCalendar> p = null; // 编译正确
    }
}

这个可以理解为<GregorianCalendar extends Comparable<Calendar>>是可以运行成功的!因为Calendar为GregorianCalendar 的父类并且GregorianCalendar 实现了Comparable<Calendar>,可查看api。

如果是如下代码则运行不成功

import java.util.GregorianCalendar;
class Demo<T extends Comparable<T>>{}
//这里把? super去掉了
public class Test
{
    public static void main(String[] args) {
       Demo<GregorianCalendar> p = null; 
        }
}

编译会报错!因为<T extends Comparable<T>>相当于<GregorianCalendar extends Comparable<GregorianCalendar>>但是GregorianCalendar并没有实现Comparable<GregorianCalendar>而是实现的Comparable<Calendar>,这里不在限制范围之内所以会报错!

总结

extends/super分别是泛型的上下界(含)。
对T的约束是Comparable的子类,对Comparable中泛型的约束是,至少是T的父类。

最后

最后提供一个完整的例子,帮助各位理解。

public class SupperDemo03 {

    interface Animal<T> {
    }

    static class EagleFish extends Bird{
    }

    static class Bird implements Animal<Bird> {
    }

    static class Fish<T extends Animal<? super T>> {

    }

    public static void main(String[] args) {

        Fish<EagleFish> fish = new Fish<>();

    }

}
更新于 2022-04-11

Z. 2年前

‘Java有泛型这一个概念,是为了初衷是为了保证’
这句话似乎应该是 ‘Java有泛型这一个概念,初衷是为了保证’

半兽人 -> Z. 2年前

感谢,已经更新了文章。

查看java更多相关的文章或提一个关于java的问题,也可以与我们一起分享文章