4

This is the Kotlin equivalent of Java code using 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 code:

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

In both case args is null , But if I run it, Kotlin code is giving Exception

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

What is the cause of this as Kotlin is using the standard Java class?

WenChao
  • 3,586
  • 6
  • 32
  • 46
  • `wrong number of arguments`: did you look at the arguments in either case? Can you `assert` the length of the array before the call? – 9000 Jan 21 '17 at 00:34
  • Hi @9000, both case 'args' is null before the print statement – WenChao Jan 21 '17 at 01:24

1 Answers1

11

When you pass args into method!!.invoke(obj, args) in Kotlin, it is actually a single argument of array type, and by default it is not decomposed into its elements as separate arguments.

To achieve that behavior instead, use the spread operator: *args

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

With this syntax, args will be passed in the same way as in Java varargs. For example, these lines of code are equivalent:

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

Note: if a method doesn't have any parameters, args will be null, and spreading it in Kotlin would result into a NullPointerException. As a workaround, use *(args ?: arrayOfNulls<Any>(0)), and in the described corner case the right part is chosen and spread into zero arguments.


My example proxy implementation:

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

And the output is:

Before; args: [1, 2]
After; result: 3
3
hotkey
  • 140,743
  • 39
  • 371
  • 326
  • Thanks for the knowledge, however if I just replace args with 'null', I'm getting the same Exception ? Any thoughts on that? – WenChao Jan 21 '17 at 01:23
  • @EugenPechanec same result :( – WenChao Jan 21 '17 at 01:27
  • 3
    @EugenPechanec Interestingly `*arrayOfNulls(0) ` worked! But why? Why is null not enough – WenChao Jan 21 '17 at 01:37
  • 2
    @WenChao, it's just the same: because `null` as passed is *one* argument, while `*arrayOfNulls(0)` is spread into none. :) – hotkey Jan 21 '17 at 01:39
  • Also, updated the answer with an example proxy implementation. – hotkey Jan 21 '17 at 01:39
  • I tried the code in the question using `*args` instead of just `args`. The latter produced an `IllegalArgumentException`, the former a `NullPointerException`. This only happens when the function you're proxying is a void function, in whose case it looks like the array in question will be null. – Edwin Dalorzo Jan 21 '17 at 01:43
  • @hotkey Yeah, in your example you have params in the function of the interface, mine doesn't have any param. So I can't use `*args` it will throw exception saying `Parameter specified as non-null is null` – WenChao Jan 21 '17 at 01:43
  • @EdwinDalorzo you are right, I'm proxying a void function with no params – WenChao Jan 21 '17 at 01:43
  • I think @hokey's answer is the right one. I will upvote now. It is just that in this case Kotlin's code will be more verbose, because we cannot spread a null array. Right? – Edwin Dalorzo Jan 21 '17 at 01:44
  • 2
    Thanks to everyone, I've updated the answer with the corner case of `args == null` explanation. – hotkey Jan 21 '17 at 01:49
  • 1
    @hotkey I guess that's probably the only solution for this now, does that mean Kotlin null is not the same as Java null at certain situation? – WenChao Jan 21 '17 at 01:51
  • @WenChao, yes, it is not the same for varargs. For example, compare Java `varargFunction(null)` (`null` array reference is passed) with Kotlin `varargFunction(null)` (single item `null` is wrapped into an array and passed). – hotkey Jan 21 '17 at 02:00
  • @hotkey Thanks ! You are really helpful – WenChao Jan 21 '17 at 02:10