Java泛型中的extends和super

1 背景

大佬勇是我这几年的技术导师,不定期向我们传授"绝世武功",当然他的格局不像我那样只在Java那么小。但最近,他却被Java泛型的通配符搞得一头雾水。事缘他最近在研究Java的java.util.Optional类,而该类有很多泛型方法,例如:

public Optional map(Function mapper)

这个方法的参数是Function实现类的对象,Function是一个函数式接口,它的唯一方法接受一个T类型T类型父类的对象,T为Optional的类型参数,返回值为UU的子类

他想彻底搞明白这个super(下界限定)和extend(上界限定)究竟是怎么回事,于是百度,结果,搜索出来的PECS原则让他更加疑惑,于是强烈要求我向他解释清楚这究竟是怎么一回事,但要说服他又谈何容易。

2 PECS原则

PECSProducer Extends Consumer Super的缩写,直译就是生产者使用extends消费者使用super

2.1 消费者

假设定义了如下方法:

public void initData(java.util.List list) {
    list.add(1);
    list.add(2L);
    list.add(3.0F);
    list.add(4.0D);
}

该方法接受一个java.util.List list参数,即list参数的类型参数最少也必须是Number类型,以该参数作为第一人称,它是一个消费者,因为,程序往它里面添加元素。由于它的类型下限Number,因此,IntegerLongFloatDouble必定可以被添加到list中。

在调用该方法时,类型参数可以是Number,也可以是Object,但绝不能是Number的子类,如下图:

Java泛型中的extends和super_第1张图片
如上图,list是类型参数是Object,符合的要求,因此可以作为initData方法的参数;list2类型参数是Number,也符合的要求。IntegerLongFloatDouble都是Number子类,而NumberObject的子类,因此,它们既能存到list中也能存到list2中了。

list3类型参数是Integer,明显不符合的要求,因此编译器会报错。

2.2 生产者

假设定义了如下方法:

public void readData(java.util.List list) {
    list.forEach(num -> {
        System.out.println(num);
    });
}

该方法很简单,就是遍历list参数并输出值,以该参数作为第一人称,它是一个生产者,因为它提供数据被程序读取。

但如果我们在遍历前往list里面添加一个Long类型的对象又会怎样呢?

Java泛型中的extends和super_第2张图片
是的,如上图所示,编译器报错了。

3 大佬的疑惑

大佬说,这太奇葩了,我的类型参数条件是,明显,LongNumber的子类,为什么不让我把300L加到集合里面呢?Java编译器的设计者脑袋被驴T了?

4 extends不能被修改的原因

4.1 泛型的初衷

我们都知道,Java的泛型只在编译期间有效,编译后所谓的,都会被替换成Object

它的作用是:

  • 帮助编译器保证类型安全,避免发生类似ClassCastException的异常,把类型转换错误的bug扼杀于编译过程中。
  • 良好的可读性,有了泛型,开发人员非常清晰知道,这个集合是存什么的,这个方法能接受怎样的参数。

4.2 extends若能修改则违反类型安全的初衷

大佬稍安勿躁,先看下面的例子:

public void readData(java.util.List list) {
    list.add(300L);
    list.forEach(num -> {
        System.out.println(num);
    });
}

我们假设,Java编译器不会对list.add(300L);提示编译错误,那么假设我们使用如下代码调用readData:

java.util.List list = new java.util.ArrayList<>();
list.add(100);
list.add(200);
readData(list);

这明显违反了保证类型安全的初衷了,为什么呢?因为list的类型参数是Integer,符合的条件。若list.add(300L);不报错,则我们预期list里面的元素都是Integer的,但list.add(300L);却往list里面插入了一个Long类型的元素,这已经违反了类型安全的初衷了。

以上是我对泛型通配符的理解,若有不正确,希望大家指出,共同学习。

你可能感兴趣的:(java,java,泛型)