Java泛型中的PECS原则(二)

618 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

上篇文章我们讲了泛型是什么,以及泛型怎么使用,回顾:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常

这次来聊一下PECS原则,也就是Producer Extends Consumer Super

src=http___aliyunoss.zjh336.cn_upload_2020_5_vQVR3u.png&refer=http___aliyunoss.zjh336.jfif

介绍PECS我们先来看一下 泛型在java编译后是什么状态

public class Test {
    public static void main(String[] args) {
        Class a = new ArrayList<Integer>().getClass();
        Class b = new ArrayList<String>().getClass();

        System.out.println(a == b);
    }
}

打印结果为 true

通过上述例子可以发现,我标注了a数组为Integer类型,b数组为String类型,但是为什么输出确是相等的呢?

因为在泛型代码内部,无法获取任何有关泛型参数类型的任何信息!,Java的泛型就是使用擦除来实现的,当你在使用泛型的时候,任何信息都被擦除,你所知道的就是你在使用一个对象。所以List< Integer>和List< String>在运行时,会被擦除成他们的原生类型List。

泛型擦除

JVM并不知道泛型的存在,因为泛型在编译阶段就已经被处理成普通的类和方法;

处理机制是通过类型擦除,擦除规则:

  1. 若泛型类型没有指定具体类型,用Object作为原始类型;
  2. 若有限定类型< T exnteds XClass >,使用XClass作为原始类型;
  3. 若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界类型XClass1作为原始类型;

Java 引入泛型擦除的原因是避免因为引入泛型而导致运行时创建不必要的类。

泛型擦除是为了弥补上一章节没讲的内容,今天要讲的是PECS是要了解泛型的上下限

extends 泛型的上限

上代码就理解了

public static void Test(WuLing car){
    Wuling wuling = car.getCar();
}

public static void Test(WuLing<? extends Car> car){
    Car wuling = car.getCar();
}

两段代码我们可以看出使用extends 定义后,我们就有了一个上限,接收实体类返回时就可以使用继承了car的所有类来进行接收。

一般像上面这种 ? 我们叫他类型通配符,extends 要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

再举一个例子,还是用车来举例子 最大类为 车辆 -》五菱宏光-》红色五菱宏光,现在有这样的一个关系。然后泛型 ? extends 五菱宏光,那么在接收时五菱宏光 与 红色五菱宏光可以接收,但是车辆不可以接收,因为车辆要比五菱宏光大。

如果还是不懂我建议你在继承类像上方一样吧他们的继承关系画出来,然后查看extends是谁,然后划线进行分割,extends对象上层为父类不可以接收,extends对象下层为子类可以接收。

extends 在方法内部不可以添加对象,也就是不能填充元素 ,因为你不知道他泛型最后定界是谁。

super 泛型的下限

super 下限与extends相反,它要求该泛型的类型,只能是实参类型,或实参类型的父类类型。

public static void Test(List<? super Car> car){
     car.add(new Wuling());
}

下限可以在方法内部添加元素,因为你的最低就是car了,而car的子类都是明确的,所以可以添加,而extends不明确你的定界在哪所以不可以添加元素。

总结

  1. super 与 extends关系是相反的
  2. super为泛型的上限,而extedns为泛型的下限
  3. 泛型是在编译前才存在的,主要用户编译前校验是否符合规范,编译后就消失为原始类型