用AI赚第一桶💰低成本搭建一套AI赚钱工具,源码可二开。 广告
[TOC] ## 泛型类 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224234745.png) ## 泛型接口 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224234848.png) ## 泛型方法 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224235032.png) ## 泛型边界 使用 extend 边界符来界定,限制泛型的类型,和下面的通配符不一样 如上面泛型方法所示,使用了 extend 关键字 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224235032.png) 这里是在泛型方法中的应用,其实可以用到泛型类或者泛型接口上 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200224235533.png) ## Super 和 extend 通配符 ![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200225001445.png) 定义了 动物类 Animal 动物类的子类 Fish 鱼 鱼的子类 Curcian 鲫鱼 动物类的子类 Dog ### 先来看 super 通配符 ``` java // 鱼的集合 List<? super Fish> list = new ArrayList<>(); list.add(new Fish()); list.add(new Crucian()); ``` <? super Fish> 规定了泛型是 Fish 或者 Fish 的父类。 这个时候,我们只能知道list 是只能存放 Fish 或者 Fish 父类的容器,这个时候如果往里面放 Fish 或者是 Fish 的子类,可以放心放进去。但是 Fish 的父类就不能放进去了,因为编译器不知道会放入Fish 的哪个父类,所以编译器直接禁止用 super 通配符加入超类对象。 同时,编译器只知道list 存的是 Fish 或者Fish 的超类,并不知道存储的是哪个超类,所以 用 super 修饰的泛型列表是允许访问的。 也就是只允许加入 Fish 或者 Fish 的子类对象(多态)。不允许加入Fish 的父类。并且不允许对 list 进行访问 ### 再来看 extend 通配符 ~~~ // 鱼的集合 List<? extends Fish> list2 = new ArrayList<>(); // 不能加入 list2.add(new Fish()); list2.add(new Crucian()); // 可以访问 System.out.println(list2.get(0).getName()); System.out.println(list2.get(2).isYouYong()); ~~~ <? super Fish> 规定了泛型是 Fish 或者 Fish 的子类。 这个时候list2 里面存放的只能是 Fish 或者 Fish 的子类,如果我们往里面加入Fish 或者 Fish 的子类,编译器并不知道我们加入是是什么 Fish 的子类,所以编辑器禁止向 extend 修饰的列表加入任何子类,同样的,也不允许加入Fish 本身,事实上不能往被 ? extend Fish 修饰的泛型加入任何值、 但是编译器知道该 list2 中存放的是 Fish 或者 Fish 的子类,所以可以对 list2 进行访问。 ## PECS 法则 P - Producor 生产者 E - extends C - Consumer 消费者 S - super 即: 1. 如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends) 2. 如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super) 如果既要存又要取,那么就不要使用任何通配符。 ## 泛型擦除 **泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除**。 比如 ~~~ List<String> s1 = new ArrayList<>(); List<Integer> i1 = new ArrayList<>(); System.out.println(s1.getClass() == i1.getClass()); ~~~ 返回的结果是 true,所以在运行的时候List 后面的泛型就被擦除了,所以就会出现上面 s1 == i1 的情况 ## 下面的可以忽略 super 主要用来添加数据的。 使用 super 修饰的数据,可以添加子类,但是由于不知道是添加的哪个子类,但是访问的时候可能会出问题 extend 主要用来访问数据的 使用 extend 修饰的数据,不可以添加数据,但是可以访问数据 ### 通配符 ? PECS原则: 使用 super 通配符,用来添加数据,只要是属于限定符后面 类的子类都可以添加。 使用 extend 通配符,用来访问数据,因为接收的数据,都可以保证是限定符后面的类的子类,可以任意的访问限定度后面类中的方法。 * 对于`List<? super Integer> l1`: * 正确的理解:`? super Integer`限定的是泛型参数. 令 l1 的泛型参数是 T, 则 T 是 Integer 或 Integer 的父类, 因此 Integer 或 Integer 的子类的对象就可以添加到 l1 中. * 错误的理解:~? super Integer限定的是插入的元素的类型, 因此只要是 Integer 或 Integer 的父类的对象都可以插入 l1 中~ * 对于`List<? extends Integer> l2`: * 正确的理解:`? extends Integer`限定的是泛型参数. 令 l2 的泛型参数是 T, 则 T 是 Integer 或 Integer 的子类, 进而我们就不能找到一个类 X, 使得 X 是泛型参数 T 的子类, 因此我们就不可以向 l2 中添加元素. 不过由于我们知道了泛型参数 T 是 Integer 或 Integer 的子类这一点, 因此我们就可以从 l2 中读取到元素(取到的元素类型是 Integer 或 Integer 的子类), 并可以存放到 Integer 中. * 错误的理解:~? extends Integer 限定的是插入元素的类型, 因此只要是 Integer 或 Integer 的子类的对象都可以插入 l2 中~ ## 泛型擦除 **泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除**。