Home > Net >  Reflection to get default value of parameter
Reflection to get default value of parameter

Time:06-13

I am trying to use reflection to get all the optional fields in a class and their default values (if any). This is my attempt:

fun getOptionalFields(componentType: KClass<out Any>): Map<String, DefaultValueData>  {
    val cons = componentType.primaryConstructor
    val constructorSetup = cons?.parameters
        ?.filterNot { it.isOptional }
        ?.associate { it to getValueForNonOptional(it) } ?: error("Something went wrong when choosing default val")
    val constructorInst = (cons.callBy(constructorSetup)::class as KClass<Any>)

    val conParams = (componentType.primaryConstructor?.parameters ?: emptyList())
        .filter { p -> p.isOptional }
        .associate { p ->
            Pair(p.name ?: "",
                DefaultValueData(
                    p.type,
// the below line crashes
                    constructorInst.memberProperties.first { m -> m.name == p.name }.get(constructorInst)
                )
            )
        }

    return conParams
}

The error: Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class

I am a bit puzzled at what get() wants me to pass if not the instance to get the value from?

CodePudding user response:

You are trying to get the value by providing a KClass instead of the actual instance.

This is a working solution based on your method signature and your code above:

data class Test(
    val required: String,
    val optional: Int = 7
)

val componentType = Test::class
val constructorInst = Test("required") // i skipped constructing the class via constructor

val conParams = (componentType.primaryConstructor?.parameters ?: emptyList())
    .filter { p -> p.isOptional }
    .associate { p ->
        Pair(p.name ?: "",
            Pair(
                p.type,
                componentType.memberProperties.first { m -> m.name == p.name }.get(constructorInst)
            )
        )
    }

println(conParams)  // <- OUTPUTS: {optional=(kotlin.Int, 7)}

Why have i removed this code?

    val constructorSetup = cons?.parameters
        ?.filterNot { it.isOptional }
        ?.associate { it to getValueForNonOptional(it) } ?: error("Something went wrong when choosing default val")
    val constructorInst = (cons.callBy(constructorSetup)::class as KClass<Any>)

The resulting object cons.callBy(constructorSetup) is unused because calling ::class on the expression rendered it useless. Additionally it is not required to perform the requested task in your question.

When updating your above code, result will look like

fun getOptionalFields(componentType: KClass<out Any>): Map<String, DefaultValueData>  {
    val cons = componentType.primaryConstructor
    val constructorSetup = cons?.parameters
        ?.filterNot { it.isOptional }
        ?.associate { it to getValueForNonOptional(it) } ?: error("Something went wrong when choosing default val")
    val constructorInst = cons.callBy(constructorSetup)  // <- removed ::class and cast

    val conParams = (componentType.primaryConstructor?.parameters ?: emptyList())
        .filter { p -> p.isOptional }
        .associate { p ->
            val value = constructorInst::class.memberProperties.first { m -> m.name == p.name }.get(constructorInst) as KProperty1<out Any, out Any>
            Pair(p.name ?: "",
                DefaultValueData(
                    p.type,
                    value
                )
            )
        }

    return conParams
}
  • Related