I'm trying to invoke a function using Kotlin reflection, but I'm getting the error:
java.lang.IllegalArgumentException: Callable expects 4 arguments, but 3 were provided.
This is the code doing the reflective call:
annotation.listeners.forEach { listener: KClass<*> ->
listener.functions.forEach { function: KFunction<*> ->
if (function.name == "before") {
function.call(annotation.action, request, response)
}
}
}
I've added types for listener
and function
just to make the question more readable.
This is the method that's being called:
fun before(action: String, request: RestRequest, response: RestResponse)
To double check that my types are correct, I did this:
if (function.name == "before") {
println(annotation.action::class)
println(request::class)
println(response::class)
}
This prints (which is the correct types needed for the before
function):
class kotlin.String class com.mycompany.RestRequest class com.mycompany.RestResponse
What should the fourth paramater be?
You are missing the "this" parameter which is the object that the method should be called with respect to.
it should be the first argument to the method
It's not obvious directly, but let's have a look at the doc of KCallable
:
/**
* Calls this callable with the specified list of arguments and returns the result.
* Throws an exception if the number of specified arguments is not equal to the size of [parameters],
* or if their types do not match the types of the parameters
*/
public fun call(vararg args: Any?): R
"Throws an exception if the number of arguments is not equal to the size of [parameters] [...]". Parameters on the other hand is a List<KParameter>
with following DOC:
/**
* Parameters required to make a call to this callable.
* If this callable requires a `this` instance or an extension receiver parameter,
* they come first in the list in that order.
*/
public val parameters: List<KParameter>
"If this callable requires a this
instance [...] [it] come[s] first in the list in that order."
Like bennyl already answered correctly, a this
instance is the first parameter, coming before the other three because the method needs an instance to be called on.
You can see it when you look at parameter
's content:
class X{
fun before(action: String, request: String, response: String)= println("called")
}
fun main(args: Array<String>) {
X::class.functions.forEach { function: KFunction<*> ->
if (function.name == "before") {
function.parameters.forEach{ println(it)}
//function.call(X(), "a", "b", "c")
}
}
}
The printed parameter
looks as follows:
instance of fun de.swirtz.jugcdemo.prepared.X.before(kotlin.String, kotlin.String, kotlin.String): kotlin.Unit
parameter #1 action of fun de.swirtz.jugcdemo.prepared.X.before(kotlin.String, kotlin.String, kotlin.String): kotlin.Unit
parameter #2 request of fun de.swirtz.jugcdemo.prepared.X.before(kotlin.String, kotlin.String, kotlin.String): kotlin.Unit
parameter #3 response of fun de.swirtz.jugcdemo.prepared.X.before(kotlin.String, kotlin.String, kotlin.String): kotlin.Unit
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With