如何使用varargs调用MethodHandle

kaq*_*qao 5 java reflection variadic-functions java-7 methodhandle

我正在尝试用MethodHandle代替反射调用,但是varargs似乎无法处理。

我的反射调用程序当前如下所示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我当前的重写尝试如下所示(Invoker暴露的接口必须保持不变):

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这在大多数情况下都可以正常工作。但是varargs破坏了一切。例如,具有以下方法:

public String test(int i, String... args) {
    return ...;
}
Run Code Online (Sandbox Code Playgroud)

和类似的参数:

Object[] args = new Object[] {10, new String[] {"aaa", "bbb"}};
Run Code Online (Sandbox Code Playgroud)

并且execute如上实施将失败。我试过的各种组合asSpreader()MethodHandles.explicitCastArguments()invoke而不是invokeWithArguments等,但没有成功。

我可以调用varargs方法的唯一方法是内联而不是作为数组提供参数。例如

handle.invokeWithArguments(10, "aaa", "bbb")
Run Code Online (Sandbox Code Playgroud)

但我无法做到这一点,并保持Invoker其当前的通用性质。

这真的不可能按照我的尝试来做吗?

更新: 在对各种情况进行基准测试之后,我决定坚持反思,因为invokeWithArguments所有测试案例中,其表现都明显较差。

Got*_*nal 6

似乎您只需要一个调用即可.asFixedArity,因为默认情况下,java将使用以下方法创建方法句柄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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

还有许多其他可能的解决方案,例如您可以向Invoker构造函数添加更多逻辑(使用静态工厂可能是个好主意),并使用asType方法来准备所需的签名,然后应该可以使用调用此方法.invokeExact以提高性能。

您也可以继续使用Method;)