Java:使用 Class.cast() 将对象转换为原始数组

JNe*_*lis 4 java arrays primitive casting object

使用 Class.cast 方法将伪装成对象的已知原始数组转换回其原始原始数组类型涉及到首先将操作分解为两个赋值,然后才能正确编译它的使用。


import java.util.Arrays;

class Scratch {
    public static void main(String[] args) {
        Object src = new int[]{1,2,3}; 
        castAndSet(src);
    }

    // assign index i of src to dest after casting src to int[]
    public static void castAndSet(Object src){ 
        int[] dst = new int[]{4,5,6};
        int i = 0; // assume array length greater than zero.
        if(dst.getClass().equals(src.getClass())){ // assert both are same class
            // src should also be an int[], so no ClassCastException to worry about

            dst[i] = ((int[])src)[i];  //this works of course
            // but can we cast and set using Class#cast method?

            // shouldn't we be able to do this?
            dst[i] = dst.getClass().cast(src)[i]; // doesn't compile
            dst[i] = (dst.getClass()).cast(src)[i]; // doesn't compile
            dst[i] = ((dst.getClass()).cast(src))[i]; // doesn't compile

            // let's break it apart
            var dstClass = dst.getClass(); // dstClass is Class<? extends int[]>
                                           // as per getClass javadocs.
            dst[i] = dstClass.cast(src)[i]; // still doesn't compile
                                            // array type expected;found capture<? extends int[]>

            // instead we have to break it apart twice
            var dstClass2 = dst.getClass(); // dstClass is Class<? extends int[]>
            var src2 = dstClass2.cast(src); // src2 is int[], magic
            dst[i] = src2[i]; // compiles.

            System.out.println(Arrays.toString(dst));
        }
        else throw new RuntimeException("objects are not of the same array class");
    }
}
Run Code Online (Sandbox Code Playgroud)

带括号的操作看起来像这个强制转换和设置应该是一个单行,但在分配给变量期间会发生一些隐式转换。使用“var”关键字,并且没有任何地方暗示显式类型来指导这种隐式转换(据我所知)。

到底是怎么回事?

Swe*_*per 7

的返回类型dst.getClassClass<? extends int[]>,而不是Class<int[]>您可能期望的类型。如果您不明白原因,请参阅此处。

\n

的返回类型Class<T>.castT,但是如果像您的代码中那样T呢?应用捕获转换,并变为,其中是新类型变量,其上限为 。这个想法是编译器不知道有关 的任何信息,除了它是 的子类型。当然,我们人类知道,除了它本身之外,没有任何亚型。? extends int[]Class<? extends int[]>Class<CAPTURE>CAPTUREint[]? extends int[]int[]int[]

\n

无论如何, 的返回类型cast都是CAPTURECAPTURE是类型变量,而不是数组类型,因此不能[i]对其使用数组索引。

\n

你能够做到:

\n
var src2 = dst.getClass().cast(src);\ndst[i] = src2[i];\n
Run Code Online (Sandbox Code Playgroud)\n

因为虽然dst.getClass().cast(src)是 类型CAPTURE,但当您将其分配给 a 时,类型投影var适用。从语言规范来看:

\n
\n

如果 LocalVariableType 是var,那么 letT是初始化表达式的类型,当它被视为没有出现在赋值上下文中时,因此是一个独立的表达式。局部变量的类型是相对于T( T\ xc2\xa74.10.5

\n
\n
\n

类型 T 相对于一组受限类型变量的向上投影定义如下:

\n

...

\n
    \n
  • 如果T是受限类型变量,则结果是 的上限的向上投影T
  • \n
\n
\n

所以CAPTURE这里被投影到它的上限int[]

\n

当然,int[] src2 = dst.getClass().cast(src);也可以编译,因为CAPTURE的上限为int[],所以int[]是 的超类型CAPTURE

\n