代码之家  ›  专栏  ›  技术社区  ›  Roman Puchkovskiy

Kotlin:将数组作为vararg参数传递时参数类型不匹配

  •  1
  • Roman Puchkovskiy  · 技术社区  · 6 年前

    interface Iface {
        fun doSomething(s: String)
    }
    
    class IfaceImpl : Iface {
        override fun doSomething(s: String) {
            println("Doing the job, s = $s")
        }
    }
    

    此外,还有两个相同(至少我看不出区别)的调用处理程序,一个在Java中,一个在Kotlin中:

    public class JavaHandler implements InvocationHandler {
        private final Iface target;
    
        public JavaHandler(Iface target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Java handler works");
            return method.invoke(target, args);
        }
    }
    
    class KotlinHandler(private val target: Iface) : InvocationHandler {
        override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {
            println("Kotlin proxy works")
            return method!!.invoke(target, args)
        }
    }
    

    它们都只是输出一些字符串,然后调用目标上的方法。

    最后,下面是我运行的代码:

    fun main(args: Array<String>) {
        val target = IfaceImpl()
        target.doSomething("one")
    
        val javaProxy = newProxy(JavaHandler(target))
        javaProxy.doSomething("two")
    
        val kotlinProxy = newProxy(KotlinHandler(target))
        kotlinProxy.doSomething("three")
    }
    
    fun newProxy(handler: InvocationHandler): Iface {
        return Proxy.newProxyInstance(Iface::class.java.classLoader, arrayOf(Iface::class.java), handler) as Iface
    }
    

    它使用两个调用处理程序创建两个java代理,并尝试使用它们。

    Doing the job, s = one
    Java handler works
    Doing the job, s = two
    Kotlin proxy works
    Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at KotlinHandler.invoke(KotlinHandler.kt:12)
        at com.sun.proxy.$Proxy0.doSomething(Unknown Source)
        at TestKt.main(Test.kt:17)
    

    我可以看到调试器在这两种情况下 args 由1个元素组成,它是 java.lang.Integer 实例。

    有趣的是,如果方法有0个参数,则错误消息不同:

    Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    

    在这种情况下, null 参数 参数(javadocs允许无参数调用)。

    我做错什么了吗?或者这是虫子?

    plugins {
        id 'org.jetbrains.kotlin.jvm' version '1.2.61'
    }
    
    1 回复  |  直到 6 年前
        1
  •  5
  •   msrd0 Gordon Linoff    6 年前

    更新: 在更新版本的Kotlin中,您可以使用 args.orEmpty() 而不是 args ?: emptyArray()

    你不能通过 args 但你需要使用 *(args ?: emptyArray()) 因为 Method.invoke 不需要数组,但需要可变参数。

    this answer 了解更多信息

    我发现问题的方式

    override fun invoke(proxy : Any?, method : Method, args : Array<Any>?) : Any?
    {
        println("Kotlin proxy works")
        return method.invoke(target, args)
    }
    
    public java.lang.Object invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]);
    Code:
       0: aload_2
       1: ldc           #12                 // String method
       3: invokestatic  #18                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: ldc           #20                 // String Kotlin proxy works
       8: astore        4
      10: getstatic     #26                 // Field java/lang/System.out:Ljava/io/PrintStream;
      13: aload         4
      15: invokevirtual #32                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      18: aload_2
      19: aload_0
      20: getfield      #36                 // Field target:LIface;
      23: iconst_1
      24: anewarray     #4                  // class java/lang/Object
      27: dup
      28: iconst_0
      29: aload_3
      30: aastore
      31: invokevirtual #41                 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
      34: areturn
    

    现在,如你所见,Kotlin确实操纵了 参数

    public Object invoke(Object proxy, Method method, Object args[]) throws Throwable
    {
        System.out.println("Java handler works");
        return method.invoke(target, args);
    }
    
    public java.lang.Object invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) throws java.lang.Throwable;
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String Java handler works
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: aload_2
       9: aload_0
      10: getfield      #2                  // Field target:LIface;
      13: aload_3
      14: invokevirtual #6                  // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
      17: areturn
    

    现在,让我们截取实际数组。我用Java代码创建了一个方法作为中介:

    public static Object invoke0(Iface target, Method method, Object args[]) throws Throwable
    {
        System.out.println("Invoking method with " + java.util.Arrays.toString(args));
        return method.invoke(target, args);
    }
    

    从Java和Kotlin执行- 而且很有效 .

    Object[] Object... .

    把我们的中介换成 对象。。。

    Invoking method with [[Ljava.lang.Object;@4b67cf4d]
    

    对象[] Object[][] ,表示类型不匹配!