Reflectively calling function and using default parameters

前端 未结 1 1806
悲哀的现实
悲哀的现实 2021-01-18 07:47

Given the following function

fun function(x: Int = 12) {
    println(\"x = $x\")
}

How can I using reflection invoke it without specifying

1条回答
  •  爱一瞬间的悲伤
    2021-01-18 08:31

    You can use the callBy, which respects the default values:

    ::function.callBy(emptyMap()) // is just function()
    

    Things will be messy if you have many parameters without default values:

    fun foo(a: Int, b: String = "") {}
    val ref = ::foo
    val params = ref.parameters
    ref.callBy(mapOf(params[0] to 1))  // is just foo(1)
    

    It will be even more boring if your function is a member function of a non-object type, or it's extension function, or it's an extension function to a type as a member function of a (other) non-object type.

    I wrote a convenient method to reduce boilerplate:

    fun  KFunction.callNamed(params: Map, self: Any? = null, extSelf: Any? = null): R {
        val map = params.entries.mapTo(ArrayList()) { entry ->
            parameters.find { name == entry.key }!! to entry.value
        }
        if (self != null) map += instanceParameter!! to self
        if (extSelf != null) map += extensionReceiverParameter!! to extSelf
        return callBy(map.toMap())
    }
    

    Usage:

    fun String.foo(a: Int, b: String = "") {}
    fun foo(a: Int, b: String = "") {}
    class Foo {
        fun bar(a: Int, b: String = "") {}
        fun String.baz(a: Int, b: String = "") {}
    }
    
    ::foo.callNamed(mapOf("a" to 0))
    String::foo.callNamed(mapOf("a" to 0), extSelf = "")
    Foo::bar.callNamed(mapOf("a" to 0), Foo())
    // function reference don't work on member extension functions
    Foo::class.declaredFunctions.find { it.name == "baz" }!!.callNamed(mapOf("a" to 0), Foo(), "") 
    

    0 讨论(0)
提交回复
热议问题