Casting a known primitive array masquerading as an Object back to it's original primitive array type using the Class.cast method involves breaking the operation up into two assignments first before it's use can be correctly compiled.
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");
}
}
Parenthesized operations seems like this cast and set should be a one liner but there is some implicit conversion going on during assignment to a variable. The 'var' keyword is used and no where is an explicit type hinted at to guide this implicit conversion from what I can tell.
What is going on?
The return type of
dst.getClassisClass<? extends int[]>, instead ofClass<int[]>which you might have expected. See here if you don't understand why.The return type of
Class<T>.castisT, but what ifTis? extends int[]like in your code? Capture conversion applies, andClass<? extends int[]>becomes aClass<CAPTURE>, whereCAPTUREis a fresh type variable, withint[]as its upper bound. The idea is that the compiler doesn't know anything about? extends int[], except that it is a subtype ofint[]. Of course, we as humans know thatint[]does not have any subtypes except itself.In any case, the return type of
castisCAPTURE.CAPTUREis a type variable, not an array type, so you cannot use array indexing[i]on it.You are able to do:
because although
dst.getClass().cast(src)is of typeCAPTURE, type projection applies when you assign that to avar. From the language specification:So
CAPTUREhere gets projected to its upper boundint[].Of course,
int[] src2 = dst.getClass().cast(src);also compiles becauseCAPTUREhas an upper bound ofint[], soint[]is a supertype ofCAPTURE.