代码之家  ›  专栏  ›  技术社区  ›  kaqqao

如何使用varargs调用MethodHandle

  •  4
  • kaqqao  · 技术社区  · 8 年前

    我试图用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);
        }
    }
    

    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);
        }
    }
    

    例如,采用如下方法:

    public String test(int i, String... args) {
        return ...;
    }
    

    这些论点有:

    Object[] args = new Object[] {10, new String[] {"aaa", "bbb"}};
    

    execute 如上所述的实现将失败。我尝试了不同的组合 asSpreader() , MethodHandles.explicitCastArguments() , invoke invokeWithArguments 等等都没有成功。

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

    handle.invokeWithArguments(10, "aaa", "bbb")
    

    调用程序 它目前拥有的。

    这真的不可能像我所尝试的那样吗?

    调用参数 表现明显较差 测试案例。

    2 回复  |  直到 7 年前
        1
  •  6
  •   GotoFinal    8 年前

    似乎你只需要一个电话 .asFixedArity 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);
            }
        }
    }
    

    还有许多其他可能的解决方案,比如可以向调用器构造函数添加更多逻辑(静态工厂可能是个好主意)并使用 asType 方法准备您想要的签名,然后您应该能够使用 .invokeExact 以获得小的性能提升。

    你也可以继续使用 Method ;)

        2
  •  0
  •   Junior Dussouillez    8 年前

    我完成了你的代码来重现你的问题,但它使用 invokeWithArguments . 也许我错过了什么?

    public class Main {
    
        public String test(int i, String... args) {
            return i + Stream.of(args).collect(Collectors.joining());
        }
    
    
        public static void main(String[] args) throws Throwable {
            Main main = new Main();
            Method method = Main.class.getMethod(
                "test",
                int.class,
                String[].class)
            Invoker invoker = new Invoker(method);
            assertEquals("1foobar", invoker.execute(main, new Object[]{1, "foo", "bar"})); // Success
        }
    
    
        public static class Invoker {
    
            private final MethodHandle handle;
    
            public Invoker(final Method delegate) throws Exception {
                this.handle = MethodHandles.lookup().unreflect(delegate);
            }
    
            public Object execute(Object target, Object[] args) throws Throwable {
                // Add the target and all arguments in a new array
                Object[] allArgs = Stream.concat(Stream.of(new Object[]{target}), Stream.of(args))
                    .toArray(Object[]::new);
                return handle.invokeWithArguments(allArgs);
            }
        }
    }