欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java语法深入理解之泛型

程序员文章站 2022-05-18 17:37:27
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