Java语法深入理解之泛型
泛型篇
1. 不要使用原生态类型
这一节主要是讲原生态类型的基本信息,原生态类型就是不带泛型参数,比如这样定义一个集合:
List list = new ArrayList();
那么就可以将其称之为原生态类型,原生态类型有着巨大的缺点,应在代码中避免使用
比如:
public static void main(String[] args) throws Exception{ List<String> list = new ArrayList<>(); unsafeAdd(list, Integer.valueOf(12)); String s = list.get(0); } private static void unsafeAdd(List list, Integer i) { list.add(i); }
上述代码在编译期不会报错,在运行期会抛出ClassCastException。由于unsafeAdd方法的参数中,List参数在使用原生态模式下,将一个Integer类型的数值加入到了原本只接受String类型的List集合中,所以最终出现运行时异常。
这一节中有一点很让人觉得有趣,它说
如果不确定或不关心类型参数,可以使用无限制的通配符类型(unbounded wildcard type),即一个问号代替,而不要使用原生态类型
即
List<?> list = new ArrayList<>(); //实际不会有这么写代码的,毫无价值的一行代码
个人理解是,由于这种方式下无法向list中添加任何值(null除外),所以只能用于读取,比如用在方法参数中防止编写代码时误操作。
2. 消除非受检的警告
要正确使用**@SuppressWarnings **注解,尽可能减小其作用范围,使用在局部变量上是最好的
3. 列表优于数组
Java中,数组是协变(covariant)的,泛型是不变(invariant)的,举个例子
Object[] arr = new Integer[1]; arr[0] = "String";
上述代码可以编译通过,但运行时会抛出ClassCastException
List<Object> list = new ArrayList<Integer>(); list.add("String");
上述代码第一行就会编译出错,这也是泛型的类型检查带来的优点
创建泛型数组是非法的,比如new E[]、new ArrayList<E>[]、new ArrayList<String>[],
将这几种类型称之为不可具体化的类型。
4. 优先考虑泛型
似乎没什么特别要注意的
5. 优先考虑泛型方法
普通的泛型方法容易理解,但也有一些很复杂但功能很强大的泛型方法存在。
public static <E extends Comparable<E>> E max(Collection<E> c) { if (c.isEmpty()) throw new IllegalArgumentException("Empty Collection"); E result = null; for (E e : c) if (result == null || e.compareTo(result) > 0) result = Objects.requireNonNull(e); return result; }
上面这个方法求一个集合中的最大值,可以看到在类型限制 <E extends Comparable<E>>
称之为递归类型限制,可以读作针对可以与自身进行比较的每个类型E。
6. 利用有限通配符提升API的灵活性
有限通配符类型指的是列如 List<? extends Number>
这种模式,使用通配符可以提升api的灵活性。
之前的max方法,如果放入List<ScheduledFuture<?>> = ...
是无法通过编译的,因为ScheduledFuture类没有实现 Comparable<ScheduledFuture>
接口,它的父类Delayed实现了 Comparable<Delayed>
接口,所以并不满足 <E extends Comparable<E>>
这个限制。
public static <E extends Comparable<? super E>> E max(Collection<? extends E> c) { if (c.isEmpty()) throw new IllegalArgumentException("Empty Collection"); E result = null; for (E e : c) if (result == null || e.compareTo(result) > 0) result = Objects.requireNonNull(e); return result; }
将类型参数修改为 <E extends Comparable<? super E>>
便可通过编译,事实上所有的都应该是 Comparable<? super E>
优先于 Comparable<E>
,因为对于集合来说,Comparable<E>
始终都是消费者(对集合中的元素排序,消费集合中的元素),而方法参数c作为提供元素的一方,当做生产者,使用 <? extends E>
7. 谨慎并用泛型和可变参数
使用可变参数可以创建泛型数组,但是很危险的事情,可能导致堆污染产生ClassCastException异常
static void dangerous(List<String>... stringLists) { List<Integer> integerList = List.of(1); Object[] o = stringLists; o[0] = integerList; String s = stringLists[0].get(0); }
上述方法中,使用可变变数创建一个泛型数组,可看到最终List<String>最终存储了Integer类型参数,运行期产生ClassCastException异常
8 . 优先考虑类型安全的异构容器
感觉很高大上,对java理解的更深了再来总结。
本文地址:https://blog.csdn.net/weixin_42721350/article/details/107892929
上一篇: 从入门到放弃的 Java 架构师面试题!