Почему Котлин выбрасывает IllegalArgumentException при использовании Прокси

Это Kotlin эквивалент Java-кода, использующего InvocationHandler:

override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {
    println("before httprequest--->" + args)
    val ret = method!!.invoke(obj, args)
    println("after httprequest--->")
    return ret
}

Java-код:

public Object invoke(Object o, Method method, Object[] args) throws Throwable {
    System.out.println("jdk--------->http" + args);
    Object  result=method.invoke(target, args);
    System.out.println("jdk--------->http");
    return result;
}

В обоих случаях args имеет значение null, но если я его запусту, код Kotlin дает исключение

Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments

В чем причина этого, поскольку Kotlin использует стандартный класс Java?

1 ответ

Решение

Когда вы проходите args в method!!.invoke(obj, args) в Kotlin это фактически один аргумент типа массива, и по умолчанию он не разлагается на его элементы как отдельные аргументы.

Чтобы добиться такого поведения, используйте оператор распространения: *args

val ret = method!!.invoke(obj, *args)

С этим синтаксисом args будет передан так же, как в Java varargs. Например, эти строки кода эквивалентны:

someVarargsFunction("a", "b", "c", "d", "e")
someVarargsFunction("a", "b", *arrayOf("c", "d"), "e")

Примечание: если метод не имеет параметров, args будет nullи распространение его в Kotlin приведет к NullPointerException, В качестве обходного пути используйте *(args ?: arrayOfNulls<Any>(0))и в описанном угловом случае правая часть выбирается и разбивается на ноль аргументов.


Мой пример реализации прокси:

interface SomeInterface {
    fun f(a: Int, b: Int): Int
}

val obj = object : SomeInterface {
    override fun f(a: Int, b: Int) = a + b
}

val a = Proxy.newProxyInstance(
        SomeInterface::class.java.classLoader,
        arrayOf(SomeInterface::class.java)) { proxy, method, args ->
    println("Before; args: " + args?.contentToString())
    val ret = method!!.invoke(obj, *(args ?: arrayOfNulls<Any>(0)))
    println("After; result: $ret")
    ret
} as SomeInterface

println(a.f(1, 2))

И вывод:

Before; args: [1, 2]
After; result: 3
3
Другие вопросы по тегам