Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.IllegalArgumentException: Callable expects 4 arguments, but 3 were provided

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?

like image 244
Jan Vladimir Mostert Avatar asked Jan 04 '23 11:01

Jan Vladimir Mostert


2 Answers

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

like image 66
bennyl Avatar answered Jan 08 '23 01:01

bennyl


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

like image 45
s1m0nw1 Avatar answered Jan 08 '23 01:01

s1m0nw1