使用多个变量调用 Java LambdaMetaFactory 方法以避免反射 [英] Java LambdaMetaFactory Method call with multiple vargs to avoid Reflection

查看:78
本文介绍了使用多个变量调用 Java LambdaMetaFactory 方法以避免反射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用 Java 反射

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

这是我正在做的反思

<块引用>

@Overridepublic T invokeReturn(final Object instance, final Object... args) 抛出异常 {尝试 {最终方法 mtd = this.getMethod();mtd.setAccessible(getModifierAccessType() != ModifierAccessType.PUBLIC);最终对象结果 = mtd.invoke(instance, args);if (getModifierAccessType() != ModifierAccessType.PUBLIC) {mtd.setAccessible(false);}返回 (T) 结果;} catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {logger.error("调用方法时出错", ex);抛出新的异常(例如getCause());}}

我想在这里添加另一个支持 LambdaMetaFactory 的方法

@Override公共 <T>T callReturn(Object instance, Object... args) 抛出异常 {尝试 {if (returnMethod == null) {最终 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();}开关(this.fncType()){案例功能:{MethodFunction<T>结果 = (MethodFunction) this.returnMethod.invoke();返回 result.call(instance);}案例功能参数:{MethodFunctionArgs<T>结果 = (MethodFunctionArgs) this.returnMethod.invoke();Object[] invokeParams = this.getInvokeParams(instance, args);返回 result.call(invokeParams);}情况无效:{最终的 VoidFunction 结果 = (VoidFunction) this.returnMethod.invoke();结果调用(实例);返回空;}默认: {最终 VoidFunctionArgs 结果 = (VoidFunctionArgs) this.returnMethod.invoke();结果调用(实例);返回空;}}} catch (Throwable ex) {抛出新的异常(例如);}}

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

@FunctionalInterface公共接口 MethodFunctionArgs{T call(Object... params) ;///试过也没有成功//T call(Object instance, Object... params) ;//VoidFunctionArgs///void call(Object instance, Object... params);}

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

感谢您的帮助...

解决方案

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

公共接口 MethodFunctionArgs;{T call(Object... params);静态字符串 someMethod(String arg1, int arg2) { return "";}//不起作用MethodFunctionArgsfunc = MethodFunctionArgs::someMethod;}

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

公共接口 MethodFunctionArgs;{T call(String arg1, int arg2);静态字符串 someMethod(Object... params) { return "";}//没问题MethodFunctionArgsfunc = MethodFunctionArgs::someMethod;}

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

公共接口 MethodFunctionArgs;{T call(String arg1, int arg2);静态字符串 someMethod(Object... params) { return "";}//没问题MethodFunctionArgsfunc = (arg1,arg2) ->someMethod(新对象[]{arg1, arg2});}

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

私有的MethodHandle句柄;公共对象 callReturn(Object... args) 抛出异常 {尝试 {如果(句柄==空){最终 MethodHandles.Lookup lookup = MethodHandles.lookup();MethodHandle h = lookup.unreflect(this.getMethod());句柄 = h.asType(h.type().generic()).asSpreader(Object[].class, h.type().parameterCount());}返回 handle.invokeExact(args);} catch (Throwable ex) {抛出新的异常(例如);}}

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

interface SpecialFunction {Map预定义 = getMap();字符串调用(int i, double d, String s);静态字符串方法1(int i,double d,String s){return "method1("+i+", "+d+", "+s+')';}静态字符串方法2(int i,double d,String s){return "method2("+i+", "+d+", "+s+')';}静态字符串方法3(int i,double d,String s){return "method3("+i+", "+d+", "+s+')';}/* 私有(使用 Java 9)*/静态 Map获取地图(){Mapmap = new HashMap<>();MethodHandles.Lookup lookup = MethodHandles.lookup();MethodType 调用 = 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()) 尝试{MethodHandle target = lookup.unreflect(m);if((m.getModifiers()&mod) == mod && target.type().equals(func))map.put(m.getName(), (SpecialFunction)LambdaMetafactory.metafactory(查找,调用",调用,func,目标,func).getTarget().invoke());} catch(Throwable ex) {抛出新的 ExceptionInInitializerError(ex);}返回 Collections.unmodifiableMap(map);}}

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 .. There is examples about getter and setter .. but there is no example for multiple parameterized method like doSomethig(String a, String b, int c) ;

here is what i am doing on reflection

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

}

i want to add another method which supports LambdaMetaFactory here what iam trying

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

without arguments I dont have any problem on switch cases default and Function, but with arguments i can't run it Here is My 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);
}

Anyway to do that? there is not a lot of example or tutorial all is only getter and setter maybe there is a way to create dynamic @functionalinterface with varags?

thanx for help...

解决方案

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

这篇关于使用多个变量调用 Java LambdaMetaFactory 方法以避免反射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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