Home > Back-end >  Get property of a Kotlin reflected object with type Any
Get property of a Kotlin reflected object with type Any

Time:10-10

I have something like this

fun validate(obj1: Any) {
   // Here I am getting the KClass of the object
   val objKClass = obj1::class
   // Here I am casting the object using KClass
   val obj1Cast = objKClass.safeCast(obj1)
   // Then I want to iterate over its properties
   for (prop in objKClass.memberProperties) {
       //And get one BigDecimal? value
       val bigDecimal: BigDecimal? = prop.get(obj1Cast) as BigDecimal?
   }
}

This doesn't work, I get in relation to prop.get(obj1Cast):

Type mismatch.
Required:
Nothing
Found:
Any?

Is there another way to access the values of the memberProperties and cast them to BigDecimal? (for example) given an object with type Any as the input for my function?

CodePudding user response:

Use generic type capture to solve this problem.

In your current code, prop is of type KProperty1<out Any, *>. This is because the type of obj1 is Any, so the type of objKClass is KClass<out Any>. There are no values that satisfy the type out Any when it's used as an input parameter, so you get a compile error.

The first step is to capture the type of obj1. We can do that by adding a generic type parameter for it, instead of using Any. Let's call the captured type T.

fun <T: Any> validate(obj1: T) {
    val objKClass: KClass<out T> = obj1::class
    val obj1Cast = objKClass.safeCast(obj1) ?: return
    for (prop in objKClass.memberProperties) {
        val bigDecimal: BigDecimal? = prop.get(obj1Cast) as BigDecimal?
    }
}

Now the type of prop is KProperty1<out T, *>. This gets us one step closer, but we still have a compile error. That's because out T can only be used for an output value, but we want to pass a T into a method parameter.

Luckily, the safeCast can help us, because it will narrow the type of the value so it exactly matches the type of the class. We just need to give it some help by using a second method to capture the exact type of the class. Let's call the narrower exact type T2.

// Here we capture the type of the object as T,
// and get its class as KClass<out T>
fun <T: Any> validate(obj1: T) {
    validate(obj1, obj1::class)
}

// Here we narrow the type of the object from T to T2
fun <T: Any, T2: T> validate(obj1: T, type: KClass<T2>) {
    val obj1Cast = type.safeCast(obj1) ?: return
    for (prop in type.memberProperties) {
        val bigDecimal: BigDecimal? = prop.get(obj1Cast) as BigDecimal?
    }
}

Now it works with no compilation errors. For more information on how this works, you can read about "generic type capturing".

  • Related