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