这两天在对比着看《java编程思想》和《Effective JAVA》(第二版)中关于泛型的问题,对里面涉及到的一个泛型转换例子感觉很费解,如下:
interface UnaryFunction<T>{
T apply(T arg);
}
public class MyTestJob{>{
private int i = 1;
private static UnaryFunction<Object> ID_FUN = new UnaryFunction<Object>() {
@Override
public Object apply(Object arg) {
return arg;
}
};
public static <T> UnaryFunction<T> identityFun(){
return (UnaryFunction<T>) ID_FUN;//(1)
}
public static <T> List<T> test(){
return (List<T>) new ArrayList<D>();//(2)
}
public static void main(String[] args){
String[] str = {"aaa","bbb","ccc"};
UnaryFunction<String> sameString = identityFun();//(1)
for(String s : str){
System.out.println(sameString.apply(s));
}
List<String> list = test();//(2)
list.add("1234");
System.out.println(list.get(0));
}
}
首先这段代码是能正常运行的,不过在函数内部会有转换警告;
如上述代码段标记(1)、(2)部分所示;
这个地方居然可以正常转换,而且能编译通过,我们都知道如下代码是不能编译通过的;
//B extends A
List<A> lsta = new ArrayList<>();
List<B> lstb = new ArrayList<>();
lsta = lstb; //编译不通过
因为泛型是不协变的,所以List<A>不是List<B>的父类,不能进行类型转换;
所以,对于上述(1)、(2)的类型转换很是疑惑,也没找到合理的解释,只能自己强行解释一波,如果有不妥的地方还请大家指正:
(1)对于(List<T>) new ArrayList<D>();这个转换能通过编译期检查,个人认为与List<?>相似,对于转换为泛型参数类型
非确定的类型在编译期执行边界类型检查,T的边界是Object类型,因此参考List<? extend Object> lst = new ArrayList<Object>(),所以检查可以通过;
(2)对于函数List<String> list = test();这个调用的时候,由于泛型类型推导的原则,test函数中的T变为String类型,
编译期的类型检查这时只检查返回值的传递赋值这一步,并不在检测函数内部的类型转换,因此编译期检查通过;
而由于运行时类型擦除原因,函数能够正常运行。
“所以,在泛型中所有动作都发生在边界处——对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型”(泛型编译期的额外检查基本上发生在值得传递(赋值)过程中)。