Java的MethodHandle.invokeExact(Object…args)接受一个可变长度的参数列表。但是,当我尝试传递Object[]数组而不是列表时,我得到了一个错误。见下文:
private void doIt() throws Throwable {
Method meth = Foo.class.getDeclaredMethods()[0];
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(meth);
Foo foo = new Foo();
String myStr = "aaa";
Integer myInt = new Integer(10);
Object [] myArray = {foo, myStr, myInt};
mh.invokeExact(foo, myStr, myInt); // prints "Called aaa 10"
mh.invokeExact(myArray); // throws Exception
}
class Foo {
public void bar(String myStr, Integer myInt) {
System.out.println("Called " + myStr + " " + myInt);
}
}
第二次调用invokeExact()会产生这个Exception:
Exception in thread "main" java.lang.invoke.WrongMethodTypeException: (Ljava/lang/String;Ljava/lang/Integer;)V cannot be called with a different arity as ([Ljava/lang/Object;)V
at io.rhubarb.core.TestInvoke.doIt0(TestInvoke.java:26)
at io.rhubarb.core.TestInvoke.main(TestInvoke.java:11)
这可能与两年前修复的Eclipse中的错误有关(https://bugs.eclipse.org/bugs/show_bug.cgi?id=385404),但我不这么认为,因为当我关闭Eclipse,删除/target目录,使用Maven重新编译所有内容,并从命令行运行时,我得到相同的结果。
我使用的是Eclipse Kepler SR2,所有内容都是最新的,以及JDK 1.7.0_25。
MethodHandle.invoke()
和MethodHandle.invokeExact()
是不同于其他变量性方法的特殊方法:
与虚方法一样,对
invokeExact
和invoke
的源代码级调用编译为invokevirtual
指令。更不寻常的是,编译器必须记录实际的参数类型,并且可能不会对参数执行方法调用转换。相反,它必须根据它们自己的未转换类型将它们压入堆栈。方法句柄对象本身被压入参数之前的堆栈中。然后编译器用一个描述参数和返回类型的符号类型描述符调用方法句柄。
所以,当你调用这些方法时,参数的类型很重要。如果你想用Object[]
来传递参数,你应该用invokeWithArguments()
来代替:
mh.invokeWithArguments(myArray);
参见:
-
MethodHandle
javadoc