PDF 课件和源代码下载地址:
https://gitee.com/geektime-geekbang/LetsJava
作者回复: ✅
作者回复: 这是Java自带的Collections类里的copy方法。dest是目标list,src是源list。从src复制到desc。 public static <T> void copy(List<? super T> dest, List<? extends T> src) { producer-extends,consumer-super(PECS)。也就是说,如果一个list(泛指使用泛型的地方,下同)是生产者,程序需要从里面取数据,就应该用extends,比如这里的src。如果一个list是消费者,需要往里面放数据,是消费者,就应该用super,比如这里的dest。 为什么呢?生产者,生产出来的数据应该有更丰富的类型信息,也就是说应该是某个类型(T)或者其子类行,这样读出来才能放心大胆的把它当作T类型使用。所以List<? extends T> src的意思是说,读吧,里面的对象要么是T,要么是T的子类,extends嘛。 消费者,规定类型的上界,只要是T或者T的父类,都应该允许写入。如果是规定下界,比如? extends T,那么会是什么类型呢?可以是T的任意子类型,那还怎么写入,比如X是T的子类,那么这个list代表的可能就是List<X>,写入的时候,如果你只知道这个list里是T的子类,但是不知是哪个子类,写入什么T的子类都有可能出错。 这个比较绕,要细细品。
作者回复: Effective Java里总结出过一个简单的抛弃这些概念的规则:producer-extends, consumer-super(PECS),想让List作为生产者提供数据,就用extends,想让List作为消费者放入数据,就用super。
作者回复: 总结的很好~
作者回复: 返回值是这种类型,你提到强转,我的理解是map的value类型。确实,这时候如果知道map的value具体是什么类型,就需要强转。当然,如果不确定,可以instanceof判断一下。
作者回复: 编译期擦除,运行时擦除可能是我一不小心说秃噜嘴了。
作者回复: 我在视频里提过一嘴,Java 的范型就做了两件事情 1)根据各种语法元素,让编译器知道一个 Object 引用允许指向什么类型的对象。什么上界下界,协变逆变,都是细节。最终的目的就是判断,一个范型的引用是不是可以指向某个具体类型的对象,或者一个范型的参数,是否可以接受某个具体的类型的引用作为实参。 2)检查完之后,就是强制类型转换。 Java 编译器会在生成的字节码里,帮我们加上强制类型转换的语句,省去我们自己写强制类型转换了。而且通过1)的检查,也“尽力”避免了出现ClassCastException的可能。 汇成一句话就是: “编译期类型检查,运行时类型擦除” 你如果去看 Java 编译出来的带范型的类文件的字节码,会发现编译器添加的强制类型转换相关的操作 P.S. :Java 的范型是后面加上去的,有些设可能是处于妥协。但是在我看来,也是够用了。
作者回复: 不得不说你理解的很到位。具体使用的时候就是一个用来读,一个用来写。你总结的里面也说明白了错误的写入或者读取,会出现什么问题。理解了就会觉得自然,赞。 “关于协变的总结就是,你只能去读取协变本身的那个类型和它的子类型的元素,不能去读取它的父类,以及它的间接父类的元素以及不能写入元素。 ” 不能去读取它的父类描述不够精确。其实任何一个子类的实例都可以当成它父类的实例来使用。 学的不错哦,加油哦~
作者回复: 别急,先看看我这篇FAQ: https://github.com/geektime-geekbang/LetsJava/blob/master/FAQ/04%E7%AB%A0-%E8%8C%83%E5%9E%8B%E5%BC%95%E7%94%A8%E7%9A%84%E9%80%9A%E9%85%8D%E7%AC%A6%E5%86%8D%E8%A7%A3.md
作者回复: 类型擦除是指运行时无法获取范型信息。范型仅仅是能用在编译期对引用类型(对应你的问题,不是具体对象的类型)进行类型检查,运行的时候,范型信息就被擦除了。