java泛型——编译期类型检查的范围

  • Post author:
  • Post category:java


这两天在对比着看《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类型,
编译期的类型检查这时只检查返回值的传递赋值这一步,并不在检测函数内部的类型转换,因此编译期检查通过;
而由于运行时类型擦除原因,函数能够正常运行。

“所以,在泛型中所有动作都发生在边界处——对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型”(泛型编译期的额外检查基本上发生在值得传递(赋值)过程中)。



版权声明:本文为huangyaa729原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。