Search code examples
reflectionkotlinkotlin-reflect

Error when use callBy on a function with default parameters in Kotlin


I try to call a function with default parameters values without put parameters in Kotlin.

For example:

class Test {
    fun callMeWithoutParams(value : Double = 0.5) = value * 0.5

    fun callIt(name: String) = this.javaClass.kotlin
            .members.first { it.name == name }
            .callBy(emptyMap())
}

fun main(args: Array<String>) {
   println(Test().callIt("callMeWithoutParams"))
}

I have the exception:

Exception in thread "main" java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun 
 Test.callMeWithoutParams(kotlin.Double): kotlin.Double
     at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod(KCallableImpl.kt:139)
    at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:111)
    at Test.callIt(Main.kt:15)
    at MainKt.main(Main.kt:20)

Strange because the parameter is not required but optional...


Solution

  • From a bit of testing, a KClass won't keep track of the actual object it was created from, the main difference being that this::class will use the runtime type of this.

    You can verify this by querying information about all the parameters:

     name | isOptional | index |     kind |          type
    -----------------------------------------------------
     null        false       0   INSTANCE            Test
    value         true       1      VALUE   kotlin.Double
    

    The first parameter is actually the instance of the class. Using this::callMeWithoutParams will keep track of this, removing the first row of the table, but naturally doesn't allow for finding the member by name. You can still call the method by providing the object:

    fun callIt(name: String) { 
        val member = this::class.members.first { it.name == name }
        member.callBy(mapOf(member.instanceParameter!! to this))
    }