在JDK 8中,我可以使用反射来调用具有FunctionalInterface参数的方法,并传递Lambda表达式。例如,这有效。
import java.util.function.IntPredicate;
import java.lang.reflect.Method;
public class LambdaReflect {
public static void main(String args[]) {
System.out.println(test(x->true));
// Now do this in reflection
Class<LambdaReflect> thisC = LambdaReflect.class;
Method meths[] = thisC.getDeclaredMethods();
Method m = meths[1]; // test method
try {
IntPredicate lamb = x->true;
boolean result = (Boolean) m.invoke(null, lamb);
System.out.println(result);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static boolean test(IntPredicate func) {
return func.test(1);
}
}
但是,如果参数类型仅在运行时已知,那么如何将lambda表达式传递给方法?换句话说,如果我在编译时不知道方法的参数类型,但只知道它是一个函数接口,我可以使用反射来用lambda表达式调用它吗?
答案 0 :(得分:4)
如果在编译时不知道目标类型,则无法创建lambda表达式。但是您可以将lambda的代码放入方法中并创建对此方法的方法引用。这类似于编译lambda表达式的方式。不同之处在于使用反射代码明确创建了功能接口实现:
import java.lang.invoke.*;
import java.util.function.IntPredicate;
import java.lang.reflect.Method;
public class LambdaReflect {
public static void main(String args[]) {
try {
for(Method m: LambdaReflect.class.getDeclaredMethods()) {
if(!m.getName().equals("test")) continue;
// we don’t know the interface at compile-time:
Class<?> funcInterface=m.getParameterTypes()[0];
// but we have to know the signature to provide implementation code:
MethodType type=MethodType.methodType(boolean.class, int.class);
MethodHandles.Lookup l=MethodHandles.lookup();
MethodHandle target=l.findStatic(LambdaReflect.class, "lambda", type);
Object lambda=LambdaMetafactory.metafactory(l, "test",
MethodType.methodType(funcInterface), type, target, type)
.getTarget().invoke();
boolean result = (Boolean) m.invoke(null, lambda);
System.out.println(result);
break;
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
private static boolean lambda(int x) { return true; }
public static boolean test(IntPredicate func) {
return func.test(1);
}
}
如果要实现任意功能签名(这意味着实现相当简单,不依赖于未知参数),您可以使用MethodHandleProxies
。区别在于MethodHandle
不需要直接,即不需要代表真实的方法。因此,您可以创建handle invariably returning a constant并使用dropArguments
插入其他形式参数,直到您拥有一个具有正确功能签名的句柄,您可以将其传递给asInterfaceInstance
答案 1 :(得分:2)
您始终可以使用类型转换来指示lambda表达式,该类型转换允许编译器推断功能接口:
m.invoke(null, (IntPredicate) (x -> true));
但是,如果您知道签名那么好,为什么要使用反射?如果要生成接口的运行时实现,则应使用运行时生成的类实现该接口。查看Java代理或我的库Byte Buddy。这样,您可以按如下方式提供参数:
IntPredicate instance = new ByteBuddy()
.subclass(IntPredicate.class)
.method(named("test")).intercept(FixedValue.value(true));
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoadedClass()
.newInstance();
m.invoke(null, instance);