Java 泛型完全解读 (2)

根据上面的讲解可以看出所谓的擦除补偿或者擦除后的修正,其大体思路都是用额外的方法告知运行时的类型信息,可以是记录到局部变量,也可以是指定参数的确切类型(Array.newInstance(Class<?> componentType, int length));

三、边界拓展

基于安全的考虑 Java 泛型是不变的(避免取出数据时的类型转换错误);

List<Object> list = new ArrayList<String>(); // error

所以在使用集合类的时候,每个集合都需要强制指定确切类型就有点不方便,比如我想指定一个集合存放 A 以及 A 的子类;在这种情况下就引入了extends,super,? 来拓展和管理泛型的边界;

1. 无界通配符 <?>

通配符主要用于泛型的使用场景(泛型一般有“声明”和“使用”两种场景);
通常情况下<?>和原生类型大致相同,就像List和List<?>的表现大部分都是一样的;但是要注意他们其实是有本质去别的,<?> 代表了某一特定的类型,但是编译器不知道这种类型是什么;而原生的表示可以是任何 Object,其中并没有类型限制;

List<?> list = new ArrayList<String>(); // correct list.add("34"); // error String s = list.get(0); // error Object o = list.get(0); // correct boolean add(E e);

上面的代码很明确的反应了这一点(<?> 代表了某一特定的类型,但是编译器不知道这种类型是什么),

因为编译器不知道这种类型是什么,所以在添加元素的时候,当然也就不能确认添加的这个类型是否正确;当使用<?>的时候,代码中的add(E e)方法,此时的 E 会被替换为 <?>,实际上编译器为了安全起见,会直接拒绝参数列表中涉及通配符的方法调用;就算这个方法没有向集合中添加元素,也会被直接拒绝;

当List<?>取出元素的时候,同样因为不知道这个特定的类型是什么,所以只能将取出的元素放在Object中;或者在取出后强转;

2. 上界 <extends>

extends,主要用于确定泛型的上界;

<T extends Test> // 泛型声明 <T extends Test & interface1 & interface2> // 声明泛型是可以确定多个上界 <? extends T> // 泛型使用时

界定的范围如图所示:

upperBounds

应当注意的是当extends用于参数类型限定时:

List<? extends List> list = new ArrayList<ArrayList>(); // correct list.add(new ArrayList()); // error List l = list.get(0); // correct ArrayList l = list.get(0); // error

上面的分析同无界通配符类似,只是 List l = list.get(0); 是正确的,是因为 <? extends List> 界定了放入的元素一定是 List 或者 list 的子类,所以取出的元素能放入 List 中,但是不能放入 ArrayList 中;

3. 下界 <super>

super,主要用于确定泛型的下界;如图所示:

lowerBounds

List<? super HashMap> list = new ArrayList<>(); // correct LinkedHashMap m = new LinkedHashMap(); // correct HashMap m1 = m; // correct Map m2 = m; // correct list.add(m); // correct list.add(m1); // correct list.add(m2); // error Map mm = list.get(0); // error LinkedHashMap mm1 = list.get(0); // error

根据图中的范围对照代码,就能很快发现Map在List<? super HashMap>的范围之外;而编辑器为了安全泛型下界集合取出的元素只能放在 Object里面;

4. PECS 原则

PECS原则是对上界和下界使用的归纳,即producer-extends, consumer-super;结合上面的两幅图,表示:

extends,只能读,相当于生产者,向外产出;

super,只能写,相当于消费者,只能接收消费;

同时边界不能同时规定上界和下界,正如图所示,他们的范围其实是一样的,只是开口不一样;

5. 自限定类型

对于上面讲的泛型边界拓展,有一个很特别的用法,

class Test<T extends Test<T>> {} public <T extends Comparable<T>> T max(List<T> list) {}

自限定类型可以通俗的解释,就是用自己限定自己,即自和自身相同的类进行某操作;如上面的 max 方法,就表示可以和自身进行比较的类型;

那么如果想要表达只要是同一祖先就能相互比较呢?

public <T extends Comparable<? super>> T max(List<? extends T> list) {}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwgss.html