Java LambdaMetaFactory Method call with multiple vargs to avoid Reflection

后端 未结 1 974
無奈伤痛
無奈伤痛 2020-12-22 08:12

I am currently using Java reflection

I don\'t have any problem doing it with reflection . I learned about LambdaMetaFactory has better performance than reflection

相关标签:
1条回答
  • 2020-12-22 08:35

    You can’t bind an interface with a varargs method to an arbitrary target method, as then, the interface would promise to handle an arbitrary number of arguments while the actual implementation method only accepts a fixed number of arguments. Hence, the following does not compile:

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

    What you can do, is the other way round, an implementation method that can handle an arbitrary number of arguments can also handle a request with specific arguments:

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

    But it must be mentioned that the LambdaMetaFactory is not capable of handling the varargs processing. The compiler helps here by inserting a synthetic helper method, so the compiled code is equivalent to

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

    But when you have a functional interface with varargs, you are already paying a lot of the typical Reflection costs (boxing, and creating and filling an array) before even invoking the interface method. You may still test the performance of Method.invoke compared to MethodHandle.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);
        }
    }
    

    To get a performance benefit from the LambdaMetaFactory, you need a specific interface with a matching functional signature.

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题