Home > Software design >  Check if generic is an array with inline function
Check if generic is an array with inline function

Time:12-20

How can I check if a Generic is an Array using an inline function?

I tried with the following code:

class Mediator {
    inline fun <reified C> mediate(string: String): Container<C> {
         if (C::class == Int::class) {
             //It works
         }
         else if (C::class == Array::class) {
             //It doesn't work!
         }
         throw IllegalStateException("Yopta")
     }
}

But it doesn't work. Maybe because it can be Array<Whatever>?

How can I do it?

CodePudding user response:

Contrary to collections where for example List<String> and List<Int> are internally represented by the same class List, in arrays the type parameter is a part of the type itself. That means Array<String> and Array<Int> are internally represented as different types and as far as I know, they don't have a common super type.

Frankly, I didn't find a pure Kotlin solution to check if a class is an array. It seems to me like an overlook in the design of the reflection API. If you don't mind using the Java reflection, you can do it like this:

else if (C::class.java.isArray) {

Update

There is one interesting fact here. In the Kotlin type system we could consider Array<out Any?> to be a supertype of all arrays. For example, we can upcast to it without an explicit cast operator:

val intArray = arrayOf(1, 2, 3)
val arr: Array<out Any?> = intArray

However, for the reflection API these two types are entirely different:

// false
println(Array<Int>::class.isSubclassOf(Array<out Any?>::class))

I assume this is due to how arrays where implemented in Java. I'm not even sure if it would be technically possible to return true in the code above. Still, it is concerning it provides a different result than the type system at a compile time and it doesn't even produce a warning.

CodePudding user response:

Actual answer that solves the issue here.

Since broot added an actual answer I'll just leave this here as a note as to how we can see that he is right basically.

If we make the call like this:

Mediator.mediate<Array<Int>>("")

Adding a simple check inside the function like this makes it a bit confusing as to why they are not equal.

println(C::class) //class Kotlin.Array
println(Array:class) //class Kotlin.Array

But doing the same for the underlying java class shows that they are not really the same object.

println(C::class.java) //class [Ljava.lang.Object
println(Array:class.java) //class [Ljava.lang.Integer

So changing the statement to:

if(C::class.java == Array<Int>::class.java)

Will make the example work ... for Int only. All other "infinite" possibilities will have to be added manually. Not an issue if you just want to check Array<X> only, but definitely not generic.

  • Related