8

Given the following function

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

How can I using reflection invoke it without specifying x (or somehow using default value, not hard coding it)?

Mibac
  • 8,990
  • 5
  • 33
  • 57

1 Answers1

14

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 <R> KFunction<R>.callNamed(params: Map<String, Any?>, 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(), "") 
glee8e
  • 6,180
  • 4
  • 31
  • 51