首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议95:强制声明泛型的实际类型

关灯直达底部

Arrays工具类有一个方法asList可以把一个变长参数或数组转变为列表,但是它有一个缺点:它所生成的List长度是不可改变的,而这在我们的项目开发中有时会很不方便。如果你期望生成的列表长度是可变,那就需要自己来写一个数组的工具类了,代码如下:


class ArrayUtils{

//把一个变长参数转变为列表,并且长度可变

public static<T>List<T>asList(T……t){

List<T>list=new ArrayList<T>();

Collections.addAll(list, t);

return list;

}

}


这很简单,与Arrays.asList的调用方式相同,我们传入一个泛型对象,然后返回相应的List,代码如下:


public static void main(Stringargs){

//正常用法

List<String>list1=ArrayUtils.asList("A","B");

//参数为空

List list2=ArrayUtils.asList();

//参数为数字和字符串的混合

List list3=ArrayUtils.asList(1,2,3.1);

}


这里有三个变量需要说明:

(1)变量list1

变量list1是一个常规用法,没有任何问题,泛型实际的参数类型是String,返回的结果也就是一个容纳String元素的List对象。

(2)变量list2

变量list2中容纳的是什么元素呢?我们无法从代码中推断出list2列表到底容纳的是什么元素(因为它传递的参数是空,编译器也不知道泛型的实际参数类型是什么),不过,编译器会很“聪明”地推断出最顶层类Object就是其泛型类型,也就是说list2的完整定义如下:


List<Object>list2=ArrayUtils.asList();


如此一来,编译时就不会给出"unchecked"警告了。现在新的问题出现了:如果期望list2是一个Integer类型的列表,而不是Object列表,因为后续的逻辑会把Integer类型加入到list2中,那该如何处理呢?

强制类型转化(把asList强制转换成List<Integer>)?行不通,虽然Java的泛型是编译擦除式的,但是List<Object>和List<Integer>没有继承关系,不能进行强制转换。

重新声明一个List<Integer>,然后读取List<Object>元素,一个一个地向下转型过去?麻烦,而且效率又低。

最好的解决方法是强制声明泛型类型,代码如下:


List<Integer>list2=ArrayUtils.<Integer>asList();


就这么简单,asList方法要求的是一个泛型参数,那我们就在输入前定义这是一个Integer类型的参数,当然,输出也是Integer类型的集合了。

(3)变量list3

变量list3有两种类型的元素:整数类型和浮点类型,那它生成的List泛型化参数应该是什么呢?是Integer和Float的父类Number?你太高看编译器了,它不会如此推断的,当它发现多个元素的实际类型不一致时就会直接确认泛型类型是Object,而不会去追索元素类的公共父类是什么,但是对于list3,我们更期望它的泛型参数是Number,都是数字嘛!参照list2变量,代码修改如下:


List<Number>list3=ArrayUtils.<Number>asList(1,2,3.1);


Number是Integer和Float的父类,先把三个输入参数向上转型为Number,那么返回的结果也就是List<Number>类型了。

通过强制泛型参数类型,我们明确了泛型方法的输入、输出参数类型,问题是我们要在什么时候明确泛型类型呢?一句话:无法从代码中推断出泛型类型的情况下,即可强制声明泛型类型。