class Test {
data class Q(val t: Array<Int>)
@Test
internal fun name() {
Q::class.primaryConstructor!!.call(Array(10) { it as Number })
}
}
Throws an exception: argument type mismatch.
A specific array type is known only at runtime. At compile time, only its superclass is known… In the example, this is Number. How can an instance of class Q be created through reflection?
thanks
There are 2 key differences between arrays and normal generics:
Unlike normal Java generics where types are erased at compile time arrays retain generic information in runtime. That’s why you get IllegalArgumentException, because you are passing instance of Number[] in place of Integer[] argument.
They are covariant. This was somewhat fixed on Kotlin’s compiler level to make arrays invariant but even so causes a slight performance penalty on JVM and can cause ArrayStoreException. Example:
val intArray: Array<Int> = arrayOf(1, 2, 3)
val unknownArray: Array<*> = intArray // an additional step to force Kotlin's compiler to lose generic information
@Suppress("UNCHECKED_CAST")
val stringArray: Array<Any> = unknownArray as Array<Any> // array is covariant on JVM so it works
stringArray[0] = "1" // throws ArrayStoreException
Thanks for the answer. But this does not work in my case
data class Q(val t: Array<Int>)
@Test
internal fun name() {
val array: Array<*> = Array(10) { it as Number }
Q::class.primaryConstructor!!.call(array)
}
I’m not exactly sure of what exactly are you asking.
This class can be instantiated reflectively as long as you pass an Array<Int> as argument, for example:
class Test {
data class Q(val t: Array<Int>)
@Test
internal fun name() {
Q::class.primaryConstructor!!.call(Array(10) { it })
}
}
I don’t know why you had a cast to Number. That won’t work for the reason given by @madmax1028
I gave a very simplified example. in fact, things are much more complicated. we can take json deserialization as an example. how to deserialize a field that is of type Array <Int>?
Serialization/mapping frameworks can read generic type information from field declarations. As for the object creation they usually contain a preregistered set of converters for primitives, arrays and collections. Other classes either come under general serialization rules or you need to declare them explicitly.
Maybe there is a better way, but this snippet should work:
for (memberProperty in Q::class.memberProperties) {
val propertyType = memberProperty.returnType.javaType as Class<*>
if (propertyType.isArray) {
val array = java.lang.reflect.Array.newInstance(propertyType.componentType, 10) as Array<*>
}
}