Search code examples
javaandroidreflectionkotlinintrospection

Get name of function argument in higher-order function


It seems that Kotlin does not support getting local functions etc. Please see the example below:

fun <T> MutableList<T>.filterOnCondition(condition: (T) -> Boolean): MutableList<T>
{ 
    // For the printline, trying to get the function name, 
    // which in this case is passedAsCondition
    println(condition.reflect()!!.instanceParameter!!.name)
}

fun passedAsCondition (number: Int, multipleOf : Int): Boolean
{
    return number % multipleOf == 0
}

numbers.filterOnCondition { passedAsCondition(it, 5) }

Kotlin returns this error as it has not been mapped out yet:

"kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Introspecting local functions, lambdas, anonymous functions and local variables is not yet fully supported in Kotlin reflection"

(https://github.com/JetBrains/kotlin/blob/master/core/reflection.jvm/src/kotlin/reflect/jvm/internal/EmptyContainerForLocal.kt#L41)

BUT, surely this is possible to do via Java, is it not?


Solution

  • It's an anonymous function, thus it's name will be <anonymous>:

    val x: (Int) -> Boolean = { passedAsCondition(it, 5) }
    println(x.reflect()?.name) //prints <anonymous>
    

    When you have a lambda { passedAsCondition(it, 5) } how would you expect the reflection to work here? passedAsCondition is a simple call made inside the lambda, but you're invoking the reflect on an unnamed, anonymous, lambda, which does not have a name.

    Ordinary functions can be used with method references which of course do have a name:

    fun x(a: Int): Boolean {
        passedAsCondition(a, 5)
        return true
    }
    println(::x.name) //gives x
    

    As a result, making use of proper reflection, the following works:

    fun main(args: Array<String>) {
        mutableListOf(1).filterOnCondition(::passedAsCondition)
    }
    
    
    fun <T> MutableList<T>.filterOnCondition(
        condition: KFunction2<@ParameterName(name = "number") Int, @ParameterName(name = "multipleOf") Int, Boolean>
    ) {
        println(condition.name)
    }
    
    fun passedAsCondition(number: Int, multipleOf: Int): Boolean = number % multipleOf == 0