Java之泛型——实践准则

Java之泛型——实践准则

简介

主要摘抄Efficient Java中关于泛型的实践准则。

准则

Don’t use raw types in new code

请不要在代码中使用原生态类型

这个很容易理解,泛型给我们带来了类型上的安全检查机制,可以使得异常在编译器就被捕获,从而避免了在运行时才能发现的ClassCastException。越早发现异常,代价越小。

    //可以向其中添加任何类型数据,取出时必须要知道类型是什么并且做强制转换,如果类型不匹配则抛出异常。
    List list = new ArrayList();

    //只能存放String类型数据,且取出的元素都是String类型(当然通过特殊手段避开编译检测的属于特例)。
    List<String> = new ArrayList<>();


    //这两种虽然效果一样,但是表达的意思不一样,第一个是可以向list中添加任意类型的元素,且不限于一种。第二个是只能向list中添加Object元素,虽然Object是所有类型的父类,但是也说明了List<Object>目的是想操作Object类型的元素。
    List list = new ArrayList();
    List<Object> list = new ArrayList<>();

Eleminate unchecked warnings

消除非受检查的警告

在日常写代码的时候经常会遇到编辑器给我们的提示:

[unchecked] unchecked conversion
found
: HashSet, required: Set<Lark>
Set<Lark> exaltation = new HashSet();

建议我们正确使用泛型,碰到这些警告的第一反应不是无视,而是尽量去解决,如上面则可以

Set<Lark> exaltation = new HashSet<Lark>();

当然有些警告是很难消除的,如果这个时候我们可以确定警告是类型安全的,那么这个时候可以使用JDK自带的注解 @SuppressWarning("unchecked")来消除编译时候的警告。
当然使用警告的时候应该尽量缩小影响范围,

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {}

从其声明中可以看出,其可以使用在类,全局变量,局部变量,方法,参数,构造方法上,所以越精确,对代码的掌控就越强。

Prefer lists to arrays

当需要使用数组或者是集合的时候,优先考虑使用集合。

创建泛型数组是非法的,因为类型不安全。可以通过下面的反证的方式来证明,假设泛型数组是合法的:

List<String>[] listStringArr = new ArrayList<>[1];(1)
List<Integer> integers = Arrays.asList(11);(2)
Object[] objArr = listStringArr;(3)
objArr[0] = integers;(4)
String s = objArr[0].get(0)(5)//ClassCastException

上面的代码中(1)创建了一个List<String>类型的数组,没有问题。
(2)创建了一个List<Integer>也没有问题
(3)将Object数组引用指向listStringArr同样是允许的,因为Object是所有类的父类
(4)将integers放入Object数组的第一个元素位置,这里开始我们就可以发现异常的地方了,此时我们已经将一个Integer的List放入到只允许存放String的容器中了。
(5)取出第一个元素,依然不是我们期望的String,而是一个Integer。抛出ClassCastException异常。
从上面看出,这样做是类型不安全的,所以不允许创建泛型数组。
原因是由数组与泛型的区别造成的:

  • 数组是协变的(covariant),直白的解释就是如果Sub是Super的子类,那么Sub[]是Super[]的子类。那么父类引用指向子类对象则是合法的,就像反证的例子中Object数组指向List数组一样。而泛型是不可变的(invariant)。如List<Number>List<Ineteger>编译之后都是List,不存在父子关系。这也是不能创建泛型数组的原因所在
  • 数组是具体化的(reified),数组会在运行时才知道并且检查他们的元素类型约束,如果企图将String存入Long数组中则会包ArrayStoreException。而泛型是通过擦除(erasure)实现的,泛型只在编译时强化他们类型信息,而在运行时擦除。擦除之后的泛型代码与普通代码没有任何区别。

Favor generic types

优先考虑泛型类

使用泛型类可以最大程度的保证类型安全。也使得程序的实现更加优雅。

Favor generic methods

优先考虑泛型方法

在可以使用泛型方法解决的时候尽量使用泛型方法,尤其适合工具方法类,比如Spring获取Bean实例时其中一个getBean方法签名:

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

Use bounded wildcard to increase API flexibility

利用有限制通配符来提升API的灵活性

有些时候需要对泛型参数的范围有一个限定从而可以进行一些既定的操作,则可以使用通配符以及统配符的边界来定义泛型的范围,但是要搞清楚通配符的边界的含义,以及使用方式。

Consider typesafe heterogeneous containers

优先考虑类型安全的异构容器

总结

泛型暂时到这里,理解不难,难的是如何正确的使用他们。泛型的使用带来的不仅仅是类型安全,还可以让代码以一种更优雅的方式呈现。

猜你喜欢

转载自blog.csdn.net/chenghuaying/article/details/50908950