Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflectively calling function and using default parameters

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)?

like image 315
Mibac Avatar asked Jun 28 '17 02:06

Mibac


People also ask

Can you assign the default values to a function parameters?

Default function parameters allow named parameters to be initialized with default values if no value or undefined is passed.

What is the default parameter in a function?

The default parameter is a way to set default values for function parameters a value is no passed in (ie. it is undefined ). In a function, Ii a parameter is not provided, then its value becomes undefined . In this case, the default value that we specify is applied by the compiler.

Can the main function have default parameters?

You can't, if you want certain values to be used, you can store them in some local variables inside 'main' and use it, just as you would use the default arguments.

Can all the parameters of a function can be default parameters?

All the parameters of a function can be default parameters.


1 Answers

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(), "") 
like image 187
glee8e Avatar answered Sep 21 '22 15:09

glee8e