Home > database >  how to call a function in kotlin from a String name?
how to call a function in kotlin from a String name?

Time:10-20

Given a list of String names, is it possible to call the proper method dynamically from a specific value?

In the example, the invokeMethod is made up but it would be what I need ideally. The first parameter is the method name to call and the second one the list of parameters.

class MethodPicker() {
    val commands = listOf("foo", "tango")

    fun messageReceiver() {
        messageParam : String = "Hi stackoverflow!"
        invokeMethod(commands.random(), messageParam) //HOW TO DO THIS IN KOTLIN?, the first parameter is the methodf name and the second one would be the parameters that the method receives
    }

    fun foo(message: String) {
        println(message   "foo!")
    }
    fun tango(message: String) {
        println(message   "tango!")
    }
}

CodePudding user response:

There's probably a way of doing this using reflection — but that should only be a last resort. (It's likely to be long-winded, fragile, slow, hard to maintain, and insecure. Handy for frameworks, plug-ins, and compiler tools; but for general programming there's usually a better approach.)

One approach here would be to store a reference to the function it should call, in addition to the name.

You could use a simple class like this to store them:

class Method(val name: String, val method: (String) -> Unit)

((String) -> Unit is the type of a function that takes a single String parameter, and doesn't return a useful value.)

Your command list could then be:

val commands = listOf(Method("foo", ::foo), Method("tango", ::tango))

(:: gives you a callable reference to a method/function. Because foo() and tango() are instance methods, you'd normally have to provide the instance to call them with, e.g. myInstance::foo — but because the list is being defined in an instance method, the compiler already knows.)

And you could call a random function with:

commands.random().method(messageParam)

It may look slightly long-winded having to list the names and references, but it's much more efficient and much safer: the compiler can check everything at run-time, and you could even translate the displayed strings into different languages without having to rewrite all the code.)

CodePudding user response:

If you actually need to accept an input from the user of the String name of the function, I suggest storing them in a Map by their names.

private val commandsByName = listOf(::foo, ::tango).associateBy { it.name }

fun messageReceiver(method: String, message: String) {
    commandsByName[method]!!(message)
    // or 
    commandsByName[method]?.invoke(message) ?: error("Unknown method: $method")
    // or
    commandsByName[method]?.invoke(message) // to fail silently
}

fun randomMessageReceiver(message: String) {
    commandsByName.values.random()(message)
}

And if you don't, just a list will do.

private val commands = listOf(::foo, ::tango)

fun randomMessageReceiver(message: String) {
    commands.random()(message)
}
  • Related