如何使用varargs调用MethodHandle [英] How to invoke a MethodHandle with varargs
问题描述
我正在尝试用MethodHandle代替反射调用,但是varargs似乎无法处理.
I'm trying to replace a reflective invocation with a MethodHandle, but varargs seem to be impossible to deal with.
我的反射调用程序当前如下所示:
My reflective invoker currently looks like this:
public class Invoker {
private final Method delegate;
public Invoker(Method delegate) {
this.delegate = delegate;
}
public Object execute(Object target, Object[] args) {
return delegate.invoke(target, args);
}
}
我当前的重写尝试如下(Invoker
公开的接口必须保持不变):
My current attempt at rewriting it looks like this (the interface the Invoker
exposes has to stay the same):
public class Invoker {
private final Method delegate;
private final MethodHandle handle;
public Invoker(Method delegate) {
this.delegate = delegate;
this.handle = MethodHandles.lookup().unreflect(delegate);
}
public Object execute(Object target, Object[] args) throws InvocationTargetException, IllegalAccessException {
Object[] allArgs = Stream.concat(Stream.of(target), Stream.of(args)).toArray(Object[]::new);
return handle.invokeWithArguments(allArgs);
}
}
这在大多数情况下都可以正常工作.但是varargs破坏了一切. 例如.有类似的方法:
And this works just fine in most cases. But varargs break everything. E.g. have a method like:
public String test(int i, String... args) {
return ...;
}
以及类似这样的参数:
Object[] args = new Object[] {10, new String[] {"aaa", "bbb"}};
如上实现的
和execute
将失败.我尝试了asSpreader()
,MethodHandles.explicitCastArguments()
,invoke
而不是invokeWithArguments
的各种组合,但均未成功.
And execute
as implemented above will fail. I tried various combinations of asSpreader()
, MethodHandles.explicitCastArguments()
, invoke
instead of invokeWithArguments
etc with no success.
我可以调用varargs方法的唯一方法是内联而不是作为数组提供参数.例如
The only way I can invoke a varargs method is to provide the arguments inline and not as an array. E.g.
handle.invokeWithArguments(10, "aaa", "bbb")
但是我不能做到这一点,并保持它目前具有的Invoker
的通用性.
but I can not do that and maintain the generic nature of the Invoker
that it currently has.
这真的不是我尝试的方式吗?
Is this really impossible to do the way I'm trying?
更新:
在对各种情况进行基准测试之后,我决定坚持反思,因为invokeWithArguments
在所有测试案例中的表现都明显较差.
UPDATE:
After benchmarking various scenarios, I decided to stick to reflection as invokeWithArguments
performs significantly worse in all tested cases.
推荐答案
似乎您只需要对.asFixedArity
的一次调用,因为默认情况下,java将使用asVarargsCollector
Seems that all you need is one call to .asFixedArity
as by default java will create method handle with asVarargsCollector
public class Main {
public static String test(int i, String... args) { return "works!"; }
public static void main(String[] args) throws Throwable {
Method method = Main.class.getMethod("test", int.class, String[].class);
System.out.println(new Invoker(method).execute(null, new Object[]{1, new String[] {"foo", "bar"} }));
}
public static class Invoker {
private final MethodHandle handle;
public Invoker(final Method delegate) throws Exception {
MethodHandle handle = MethodHandles.lookup().unreflect(delegate);
if (Modifier.isStatic(delegate.getModifiers())) { // for easy static methods support
handle = MethodHandles.dropArguments(handle, 0, Object.class);
}
this.handle = handle.asFixedArity();
}
public Object execute(Object target, Object[] args) throws Throwable {
Object[] allArgs = new Object[args.length + 1];
allArgs[0] = target;
System.arraycopy(args, 0, allArgs, 1, args.length);
return handle.invokeWithArguments(allArgs);
}
}
}
还有许多其他可能的解决方案,例如您可以向Invoker构造函数添加更多逻辑(使用静态工厂可能是个好主意),并使用asType
方法准备所需的签名,然后您应该可以使用.invokeExact
以获得较小的性能提升.
There is also many other possible solutions, like you can add more logic to Invoker constructor (static factory might be a good idea) and use asType
method to prepare signature you want and then you should be able to call this using .invokeExact
to get small performance boost.
您还可以继续使用Method
;)
这篇关于如何使用varargs调用MethodHandle的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!