(这很难搜索,因为结果都是关于"方法参考")
我希望获得一个lambda表达式的Method
实例,以便与基于遗留反射的API一起使用。应该包含clousure,因此调用thatMethod.invoke(null, ...)
应该与调用lambda具有相同的效果。
我看过MethodHandles.Lookup,但它似乎只与逆转换相关。但我想bind
方法可能有助于包括clousure?
编辑:
说我有lambda experssion:
Function<String, String> sayHello = name -> "Hello, " + name;
我有一个遗留框架(SpEL),它有一个像
这样的APIregisterFunction(String name, Method method)
将调用给定的Method
而没有this
参数(即假定为静态的方法)。因此,我需要获得一个包含lambda逻辑+ clousure数据的特殊Method
实例。
答案 0 :(得分:10)
如果你找不到优雅的方式,这就是丑陋的方式(Ideone)。涉及反思时的常见警告:未来版本可能会中断等。
public static void main(String[] args) throws Exception {
Function<String, String> sayHello = name -> "Hello, " + name;
Method m = getMethodFromLambda(sayHello);
registerFunction("World", m);
}
static void registerFunction(String name, Method method) throws Exception {
String result = (String) method.invoke(null, name);
System.out.println("result = " + result);
}
private static Method getMethodFromLambda(Function<String, String> lambda) throws Exception {
Constructor<?> c = Method.class.getDeclaredConstructors()[0];
c.setAccessible(true);
Method m = (Method) c.newInstance(null, null, null, null, null, 0, 0, null, null, null, null);
m.setAccessible(true); //sets override field to true
//m.methodAccessor = new LambdaAccessor(...)
Field ma = Method.class.getDeclaredField("methodAccessor");
ma.setAccessible(true);
ma.set(m, new LambdaAccessor(array -> lambda.apply((String) array[0])));
return m;
}
static class LambdaAccessor implements MethodAccessor {
private final Function<Object[], Object> lambda;
public LambdaAccessor(Function<Object[], Object> lambda) {
this.lambda = lambda;
}
@Override public Object invoke(Object o, Object[] os) {
return lambda.apply(os);
}
}
答案 1 :(得分:4)
嗯,lambda表达式在编译期间被去掉了方法,只要它们不捕获this
(不访问非static
成员),这些方法将是{{1} }。棘手的部分是获取这些方法,因为功能接口实例与其目标方法之间没有可检查的连接。
为了说明这一点,这里是最简单的案例:
static
这很顺利,因为只有一个lambda表达式,因此只有一个候选方法。该方法的名称是特定于编译器的,可能包含一些序列号或哈希码等。
on kludge是使lambda表达式可序列化并检查其序列化形式:
public class LambdaToMethod {
public static void legacyCaller(Object arg, Method m) {
System.out.println("calling Method \""+m.getName()+"\" reflectively");
try {
m.invoke(null, arg);
} catch(ReflectiveOperationException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) throws URISyntaxException
{
Consumer<String> consumer=s -> System.out.println("lambda called with "+s);
for(Method m: LambdaToMethod.class.getDeclaredMethods())
if(m.isSynthetic() && m.getName().contains("lambda")) {
legacyCaller("a string", m);
break;
}
}
}
然而,这是有效的,可序列化的lambdas价格很高。
最简单的解决方案是在迭代方法时为要找到的lambda表达式的参数添加注释,但是,目前,static Method lambdaToMethod(Serializable lambda) {
for(Class<?> cl=lambda.getClass(); cl!=null; cl=cl.getSuperclass()) try {
Method m=cl.getDeclaredMethod("writeReplace");
m.setAccessible(true);
try {
SerializedLambda sl=(SerializedLambda)m.invoke(lambda);
return LambdaToMethod.class.getDeclaredMethod(sl.getImplMethodName(),
MethodType.fromMethodDescriptorString(sl.getImplMethodSignature(),
LambdaToMethod.class.getClassLoader()).parameterArray());
} catch(ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
} catch(NoSuchMethodException ex){}
throw new AssertionError();
}
public static void main(String[] args)
{
legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable)
s -> System.out.println("first lambda called with "+s)));
legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable)
s -> System.out.println("second lambda called with "+s)));
}
没有正确存储注释,另请参阅{{3关于这个话题。
但您也可以考虑创建保存代码而不是lambda表达式的普通javac
方法。获取方法的static
对象是直截了当的,您仍然可以使用方法引用创建一个功能接口实例...