java.util.Arrays.copyOf 泛型捕获和强制类型转换

        先来看看Arrays.copyOf方法的源码

    @IntrinsicCandidate
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

        可以说是相当匪夷所思。

        第一个点就是为什么newType==Object[].class为什么要强转一下。因为newType这里被捕获了,所以这里newType的值是Class<X1[]>这里X1表示某种未知类型,所以尽管Object[]是X1[]的父类,但是Class<Object[]>并不是Class<X1[]>的父类,在stackOverFlow上有一篇这样的问题,回答也挺完整的。所以这里把两个值都转成了Object类型。上面那个回答里给出的另一种强转方式:

(newType == (Class<? extends Object[]>)Object[].class)

也是类似的理解,首先newType是Class<X1[]>类型,Object[].class显然是Class<Object[]>类型,所以是Class<? extends Object[]>的子类型(注意这里是通配符不是捕获之后的类型,Class<X1[]>表示的是某种未知类型,而Class<? extends T[]>是个单独的类型,两者是不同的东西,前者是后者的子类型),所以这个强转没问题,然后因为newType类型Class<X1[]>也是Class<? extends Object[]>的子类型,所以两者可以比较。

        那究竟什么时候会发生捕获呢,这里讲的很清楚,我看下来大概意思就是:只有纯粹作为赋值表达式左值的时候不会发生捕获,而要求值或者调用方法等其他情况都会发生捕获,看下面这个例子,这里出错的行都标出来了

  public static void test(ArrayList<? extends B> list) {
    //继承关系C继承B继承A
    list = new ArrayList<A>();    //这是第38行
    list = new ArrayList<B>();
    list = new ArrayList<C>();

    ArrayList<A> newListA = (ArrayList<A>) list;    //这是第42行
    ArrayList<B> newListB = (ArrayList<B>) list;
    ArrayList<C> newListC = (ArrayList<C>) list;

    var res = list.get(0);
    list.set(0, list.get(0));    //这是第47行
  }

         打开命令行用javac编译一下,或者直接放在IDEA里面运行,会得到这样的结果

        IDEA里是这样的

                 其实还是挺清楚地,有CAP标志的就是捕获了,? extends ...的就是未捕获的。很明显,list为赋值表达式左值时还是? extends ...类型,未捕获,为右值时就是捕获了,命令行显示的是<CAP#1>,46行,47行调用方法时其实都捕获了,只不过这里标注的不是很清楚,打开-Xdiags:verbose就很清楚了(javac Hello.java -Xdiags:verbose),是这样的

         显然确实都是捕获了,至于47行的报错,原因是这是个? extends ...是个上限通配符,所以不接受任何参数,不过作为返回值是可以的,可以看这篇文章,JAVA核心卷一的泛型哪一章最末尾也稍微讲了一些,不过并不是那么好理解。

        另外值得一提的一点就是,泛型的强制类型转换,这一点其实上面那第二个错误已经很清楚了,首先你如果要对泛型参数进行强制类型转换,那肯定是会发生捕获,因为赋值表达式左边不会有强制类型转换,比如上面(ArrayList<A>) list就会报错,因为此时list的类型是ArrayList<X1>是发生了捕获的。那ArrayList<X1>到(ArrayList<A>)会不会报错呢,当然会,因为X1是B的子类型,而A是B的父类型,所以肯定转不了,记住ArrayList<A>和ArrayList<B>和ArrayList<C>之间并没有继承关系。那转到ArrayList<B>和ArrayList<C>为啥不报错呢?因为这是有可能成功的,比如你传进来的刚好就是一个ArrayList<B>类型的,这当然可以,因为参数只要求是ArrayList<? extends B>,那么它就可以转成ArrayList<B>(自己转自己嘛)。同理也可能刚好传进来一个ArrayList<C>。但是不管你怎么传,ArrayList<A>肯定是转不了的,所以会报错。这就不是编译器能判断的了,只有运行的时候才判断的了,所以这里会有个非检查型错误。

        至于为什么要判断它是不是Object[],当然也是为了优化效率,Stack Overflow那篇答主也讲得很清楚,因为反射操作效率不高,这里讲了(stackoverflow那篇文章的答主说的,我并没有看)

        Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.

         
/***
 *_______________#########_______________________
 *______________############_____________________
 *______________#############____________________
 *_____________##__###########___________________
 *____________###__######_#####__________________
 *____________###_#######___####_________________
 *___________###__##########_####________________
 *__________####__###########_####_______________
 *________#####___###########__#####_____________
 *_______######___###_########___#####___________
 *_______#####___###___########___######_________
 *______######___###__###########___######_______
 *_____######___####_##############__######______
 *____#######__#####################_#######_____
 *____#######__##############################____
 *___#######__######_#################_#######___
 *___#######__######_######_#########___######___
 *___#######____##__######___######_____######___
 *___#######________######____#####_____#####____
 *____######________#####_____#####_____####_____
 *_____#####________####______#####_____###______
 *______#####______;###________###______#________
 *________##_______####________####______________
 */

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值