【问题标题】:Java LambdaMetaFactory Method call with multiple vargs to avoid Reflection使用多个 vargs 调用 Java LambdaMetaFactory 方法以避免反射
【发布时间】:2018-02-17 02:15:54
【问题描述】:

我目前正在使用 Java 反射

使用反射没有任何问题。我了解到 LambdaMetaFactory 比反射具有更好的性能.. 有关于 getter 和 setter 的示例.. 但是没有像 doSomethig(String a, String b, int c) 这样的多参数化方法的示例;

这是我正在做的反思

@Override
public  T invokeReturn(final Object instance, final Object... args) throws Exception {

    try {
        final Method mtd = this.getMethod();
        mtd.setAccessible(getModifierAccessType() != ModifierAccessType.PUBLIC);
        final Object result = mtd.invoke(instance, args);
        if (getModifierAccessType() != ModifierAccessType.PUBLIC) {
            mtd.setAccessible(false);
        }
        return (T) result;
    } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        logger.error("Error while Invoking Method", ex);
        throw new Exception(ex.getCause());
    }

}

我想在这里添加另一个支持 LambdaMetaFactory 的方法我正在尝试什么

@Override
public <T> T callReturn(Object instance, Object... args) throws Exception {
    try {
        if (returnMethod == null) {
            final MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle methodHandle = lookup.unreflect(method);
            MethodType fncType = this.mtdType();
            MethodType type = this.callMethodType();
            this.returnMethod = LambdaMetafactory.metafactory(lookup, "call", fncType, type, methodHandle, methodHandle.type()).getTarget();
        }

        switch (this.fncType()) {
            case Function: {
                MethodFunction<T> result = (MethodFunction<T>) this.returnMethod.invoke();
                return result.call(instance);
            }
            case FunctionArgs: {
                MethodFunctionArgs<T> result =  (MethodFunctionArgs<T>) this.returnMethod.invoke();
                 Object[] invokeParams = this.getInvokeParams(instance, args);
                return result.call(invokeParams);
            }
            case Void: {
                final VoidFunction result = (VoidFunction) this.returnMethod.invoke();
                result.call(instance);
                return null;
            }
            default: {
                final VoidFunctionArgs result = (VoidFunctionArgs) this.returnMethod.invoke();
                result.call(instance);
                return null;
            }
        }

    } catch (Throwable ex) {
        throw new Exception(ex);
    }
}

没有参数我在 switch case default 和 Function 上没有任何问题,但是有参数我不能运行它 这是我的 MethodFunctionArgs @FunctionalInterfaces

@FunctionalInterface
public interface MethodFunctionArgs<T> {

    T call(Object... params) ;
    ///tried too no success
    //T call(Object instance, Object... params) ;
    //VoidFunctionArgs  
    ///void call(Object instance, Object... params);
}

无论如何要这样做?没有很多示例或教程,只有 getter 和 setter 也许有一种方法可以使用可变参数创建动态@functionalinterface?

感谢帮助...

【问题讨论】:

    标签: java reflection lambda-metafactory


    【解决方案1】:

    您不能将带有可变参数方法的接口绑定到任意目标方法,因为那样的话,接口将承诺处理任意数量的参数,而实际的实现方法只接受固定数量的参数。因此,以下内容无法编译:

    public interface MethodFunctionArgs<T> {
        T call(Object... params);
        static String someMethod(String arg1, int arg2) { return ""; }
        // does not work
        MethodFunctionArgs<String> func = MethodFunctionArgs::someMethod;
    }
    

    你可以做的是,反过来,一个可以处理任意数量参数的实现方法也可以处理带有特定参数的请求:

    public interface MethodFunctionArgs<T> {
        T call(String arg1, int arg2);
        static String someMethod(Object... params) { return ""; }
        // no problem
        MethodFunctionArgs<String> func = MethodFunctionArgs::someMethod;
    }
    

    但必须提到LambdaMetaFactory 无法处理可变参数处理。编译器在这里通过插入一个合成的辅助方法来提供帮助,所以编译后的代码相当于

    public interface MethodFunctionArgs<T> {
        T call(String arg1, int arg2);
        static String someMethod(Object... params) { return ""; }
        // no problem
        MethodFunctionArgs<String> func = (arg1,arg2) -> someMethod(new Object[]{arg1, arg2});
    }
    

    但是当你有一个带有可变参数的函数式接口时,你甚至在调用接口方法之前就已经支付了很多典型的反射成本(装箱、创建和填充数组)。与MethodHandle.invoke 相比,您仍然可以测试Method.invoke 的性能:

    private MethodHandle handle;
    public Object callReturn(Object... args) throws Exception {
        try {
            if(handle == null) {
                final MethodHandles.Lookup lookup = MethodHandles.lookup();
                MethodHandle h = lookup.unreflect(this.getMethod());
                handle = h.asType(h.type().generic())
                          .asSpreader(Object[].class, h.type().parameterCount());
            }
            return handle.invokeExact(args);
        } catch (Throwable ex) {
            throw new Exception(ex);
        }
    }
    

    要从 LambdaMetaFactory 获得性能优势,您需要一个具有匹配功能签名的特定接口。

    interface SpecialFunction {
        Map<String,SpecialFunction> PREDEFINED = getMap();
    
        String call(int i, double d, String s);
    
        static String method1(int i, double d, String s) {
            return "method1("+i+", "+d+", "+s+')';
        }
        static String method2(int i, double d, String s) {
            return "method2("+i+", "+d+", "+s+')';
        }
        static String method3(int i, double d, String s) {
            return "method3("+i+", "+d+", "+s+')';
        }
        /* private  (with Java 9) */ static Map<String,SpecialFunction> getMap() {
            Map<String,SpecialFunction> map = new HashMap<>();
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodType invoked = MethodType.methodType(SpecialFunction.class);
            MethodType func = MethodType.methodType(String.class,
                                                    int.class, double.class, String.class);
            final int mod = Modifier.PUBLIC|Modifier.STATIC;
            for(Method m: SpecialFunction.class.getDeclaredMethods()) try {
                MethodHandle target = lookup.unreflect(m);
                if((m.getModifiers()&mod) == mod && target.type().equals(func))
                    map.put(m.getName(), (SpecialFunction)LambdaMetafactory.metafactory(
                        lookup, "call", invoked, func, target, func).getTarget().invoke());
            } catch(Throwable ex) {
                throw new ExceptionInInitializerError(ex);
            }
            return Collections.unmodifiableMap(map);
        }
    }
    

    【讨论】:

    • 非常感谢。我读了你的答案。 LambdaMetaFactory 不适合我的用例。因为我不知道将使用哪些类(它正在从 java 类创建 web 路由)。那么根据您的经验,方法处理与反射哪个更快? ..如果你是我,你会更喜欢方法处理还是反射方法?
    • @Saltuk 通常,方法句柄更快,但是,最好是根据您的实际用例尝试和衡量。
    猜你喜欢
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 2023-01-03
    • 1970-01-01
    相关资源
    最近更新 更多