如何使用varargs调用MethodHandle [英] How to invoke a MethodHandle with varargs

查看:107
本文介绍了如何使用varargs调用MethodHandle的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆