Java:使用Class.cast()将Object转为基本类型数组的编译问题
当需要把伪装成Object的已知基本类型数组通过Class.cast()转换回原类型数组时,会发现必须拆分为两次赋值才能正常编译。像dst[i] = dst.getClass().cast(src)[i]这种链式调用无法通过编译,但用var把cast()的结果赋值给变量后,却能自动完成隐式转换直接访问元素。下面是相关代码示例:
import java.util.Arrays; class Scratch { public static void main(String[] args) { Object src = new int[]{1,2,3}; castAndSet(src); } // 将src转换为int[]后,把第i个元素赋值给dest的第i个位置 public static void castAndSet(Object src){ int[] dst = new int[]{4,5,6}; int i = 0; // 假设数组长度大于0 if(dst.getClass().equals(src.getClass())){ // 确认两者是同一类 // src肯定是int[],不用担心ClassCastException dst[i] = ((int[])src)[i]; // 这种写法当然可行 // 但能不能用Class#cast方法来实现呢? // 为什么下面这些写法不行? dst[i] = dst.getClass().cast(src)[i]; // 编译失败 dst[i] = (dst.getClass()).cast(src)[i]; // 编译失败 dst[i] = ((dst.getClass()).cast(src))[i]; // 编译失败 // 拆成两步试试 var dstClass = dst.getClass(); // dstClass的类型是Class<? extends int[]>(根据getClass文档) dst[i] = dstClass.cast(src)[i]; // 还是编译失败:提示需要数组类型,实际是capture<? extends int[]> // 必须拆成两次赋值才行 var dstClass2 = dst.getClass(); // dstClass类型是Class<? extends int[]> var src2 = dstClass2.cast(src); // src2自动推断为int[] dst[i] = src2[i]; // 编译通过 System.out.println(Arrays.toString(dst)); } else throw new RuntimeException("对象不是同一数组类型"); } }
核心原因:类型推断的上下文差异
Java的类型推断在变量赋值场景和表达式链式调用场景下的行为完全不同:
变量赋值时的类型推断
当你用var src2 = dstClass2.cast(src);时,编译器会利用dstClass2的实际运行时类型(Class<int[]>,因为dst是int[],getClass()返回的是数组的运行时类)进行目标类型推断。虽然dstClass2的声明类型是Class<? extends int[]>,但编译器可以通过赋值上下文推导出cast()方法的返回类型就是int[]——因为dstClass2本质上是int[]的类对象,cast()必然返回int[]实例。链式调用时的类型限制
在dst.getClass().cast(src)[i]这种表达式中,cast()方法的返回类型被编译器限定为capture<? extends int[]>(捕获通配符类型)。这是因为dst.getClass()的静态类型是Class<? extends int[]>,编译器无法在链式调用的表达式中直接将通配符类型具体化。而Java不允许对通配符捕获的数组类型直接使用下标访问([i])——因为编译器无法确定它具体是int[]还是某个(虽然实际不存在的)int[]子类,所以会抛出“array type expected; found capture<? extends int[]>”的编译错误。
补充说明:基本类型数组的特殊性
这里要注意:基本类型数组(比如int[])没有子类,Class<? extends int[]>实际上只能是Class<int[]>。但编译器的静态类型检查不会考虑这一点——它只会严格遵循泛型规则,不会利用运行时的实际类型信息放宽静态检查。所以即使你通过dst.getClass().equals(src.getClass())确认了类型一致,编译器也不会在链式调用中跳过通配符类型的限制。
内容的提问来源于stack exchange,提问作者JNellis

