Java 如何调用未知类型的lambda
假设我们有一组包含方法引用的变量:Java 如何调用未知类型的lambda,java,Java,假设我们有一组包含方法引用的变量: public double m2(String s, int n){return n;} Runnable r = ()->{}; Consumer<String> c1 = System.out::println; BiFunction<String, Integer, Double> f2 = this::m2; 现在,我需要一个全局方法来调用任何类型的引用方法: public static Object call(Obje
public double m2(String s, int n){return n;}
Runnable r = ()->{};
Consumer<String> c1 = System.out::println;
BiFunction<String, Integer, Double> f2 = this::m2;
现在,我需要一个全局方法来调用任何类型的引用方法:
public static Object call(Object lambda, Object... args) {...}
Object res0 = call(r); // returns null
Object res1 = call(c1, "Hello");
Object res2 = call(f2, "Hello", 1)
如何实现这个方法?参数
lambda
的一组可能的功能类型不受限制。您需要识别对象的类型并适当地调用/调用它。以下方法检查可运行
、可调用
、方法
以及任何带有功能接口
注释的内容。如果类型为Method
,它将假定第一个参数是要在其上调用方法的对象
public Object callWhatever(final Object o, final Object... params) throws Exception
{
if (o instanceof Runnable) {
((Runnable)o).run();
return null;
}
if (o instanceof Callable) {
return ((Callable<?>)o).call();
}
if (o instanceof Method) {
return ((Method)o).invoke(params[0], Arrays.copyOfRange(params, 1, params.length));
}
final Method method = getMethodForFunctionalInterface(o);
if (method != null) {
return method.invoke(o, params);
}
throw new InvalidParameterException("Object of type " + o.getClass() + " is not callable!");
}
请注意,Java允许没有functionanterface
注释的自定义函数接口,并且该方法将检测不到该接口
现在你可以称之为:
public void test() throws Exception {
final Runnable runnable = ()->{System.out.println("r called");};
final Callable<Integer> callable = ()->{System.out.println("c called"); return 123456; };
final Consumer<String> consumer = System.out::println;
final BiFunction<String, Integer, Double> bifunction = this::m2;
final Object test1 = callWhatever(runnable);
final Object test2 = callWhatever(callable);
final Object test3 = callWhatever(consumer, "Hello World");
final Object test4 = callWhatever(bifunction, "Hello", 123);
final Object test5 = callWhatever(this.getClass().getDeclaredMethod("m2", String.class, int.class), this, "Hello", 234);
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);
System.out.println(test4);
System.out.println(test5);
}
public void test()引发异常{
final Runnable Runnable=()->{System.out.println(“r调用”);};
final Callable Callable=()->{System.out.println(“c called”);返回123456;};
最终消费者=System.out::println;
最终双功能双功能=此::m2;
最终对象test1=callwhere(runnable);
最终对象test2=callwhater(可调用);
最终对象test3=callwhere(消费者,“Hello World”);
最终对象test4=callwhater(双函数,“Hello”,123);
最终对象test5=callwhater(this.getClass().getDeclaredMethod(“m2”,String.class,int.class),this,“Hello”,234);
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);
System.out.println(test4);
System.out.println(test5);
}
输出:
r调用c调用
你好,世界
空
123456
空
123.0
234.0 “Lambda表达式”不是一种值类型(如“object”或“int”)。它是一种表示值的方式,就像字符串文字一样 在Java中,不能编写“采用字符串文字的方法”——只能编写采用字符串的方法。您无法判断参数是否是使用文字创建的 这与lambdas完全相同-您不能编写“接受lambda”或将lambdas视为某种特殊情况的方法。一旦使用lambda创建接口实例,它的lambda特定属性就消失了,至少从Java语义的角度来看是这样 简而言之,您试图实现的并不是对lambdas的合理使用——您需要反思或重新思考您的架构 --编辑:澄清两点
(再次编辑,感谢Sarel Foyerlicht指出了一个错误)注意:此答案基于答案,但有不同的方法 在java中,Lambda表达式由 @功能接口 这是一条规则 函数接口只有一个抽象方法 因此,我们可以检查对象是否遵循该规则,然后调用该方法
public class Example {
public Example() throws Exception {
final Runnable runnable = ()->{System.out.println("r called");};
final Callable<Integer> callable = ()->{System.out.println("c called"); return 1000; };
final Consumer<String> consumer = System.out::println;
final BiFunction<String, Integer, Double> bifunction = this::m2;
final InterfaceWithDefault inter =()->{System.out.println("print");};
final Object test1 = call(runnable);
final Object test2 = call(callable);
final Object test3 = call(consumer, "Hello World");
final Object test4 = call(bifunction, "Hello", 123);
final Object test5 = call(inter);
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);
System.out.println(test4);
System.out.println(test5);
}
public static Object call(Object object, Object... param) throws Exception {
Method method = null;
int publicMethods=0;
for (Method m:object.getClass().getDeclaredMethods()) {
if(Modifier.isPublic(m.getModifiers())&&!m.isDefault()){
method=m;
publicMethods++;
}
}
if(publicMethods==1)
{
Object returnValue = method.invoke(object, param);
return returnValue;
}
throw new InvalidParameterException("Object of type"+ object.getClass()+" is not callable!");
}
public double m2(String s, int n){return n;}
public interface InterfaceWithDefault{
public void print();
default void defaultBar() {System.out.println("x");}
}
}
以及输出:
r called
c called
Hello World
print
null
1000
null
123.0
null
注1:我们不需要检查抽象,因为我们的对象已经覆盖了它,但是不能有超过1个公共方法,除非其他方法是默认方法(我想不出任何情况)注2:它也可以只使用1个公共方法运行类此方法已经有了实现。它被称为。您可以
getParameterCount()
,getParameterTypes()
和getReturnType()
来确定如何调用此“lambda”。除了直接的反射之外,你还希望得到答案吗?也许java脚本API会是一个更好的工具:可以在脚本引擎中设置变量等等。@AJNeufeld为了使用方法#invoke(…)
,我们需要从lambda中提取方法。天真的尝试methodm=(Method)r代码>导致异常java.lang.ClassCastException:org.df4j.reflect.FindActionTest$$Lambda$1/445884362无法强制转换为java.lang.reflect.Method
@LouisWasserman直截了当的反射会很好,编译器将把满足函数接口定义的任何接口视为函数接口,而不管接口声明中是否存在{@code functioninterface}注释。*也许您只需要检查是否只有一个公共摘要method@SarelFoyerlicht说得好,我已经澄清了我的答案,这对于没有注释的自定义接口不起作用。如果我们遵循函数接口只允许有一个公共抽象方法的规则,公共静态对象调用(对象对象,对象…参数)抛出异常{Method Method=null;int publicMethods=0;for(方法m:object.getClass().getDeclaredMethods()){if(Modifier.isPublic(m.getModifiers())&&&!m.isDefault()){Method=m;publicMethods++;}}}if(publicMethods==1){object returnValue=Method.invoke(object,param);returnValue;}抛出新的InvalidParameterException(“类型为“+Object.getClass()+”的对象不可调用!”;}通过“lambda”我指的是可以通过上述方法之一获得的任何值:lambda表达式或方法引用。@AlexeiKaigorodov:这正是你忽略的地方。有
public class Example {
public Example() throws Exception {
final Runnable runnable = ()->{System.out.println("r called");};
final Callable<Integer> callable = ()->{System.out.println("c called"); return 1000; };
final Consumer<String> consumer = System.out::println;
final BiFunction<String, Integer, Double> bifunction = this::m2;
final InterfaceWithDefault inter =()->{System.out.println("print");};
final Object test1 = call(runnable);
final Object test2 = call(callable);
final Object test3 = call(consumer, "Hello World");
final Object test4 = call(bifunction, "Hello", 123);
final Object test5 = call(inter);
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);
System.out.println(test4);
System.out.println(test5);
}
public static Object call(Object object, Object... param) throws Exception {
Method method = null;
int publicMethods=0;
for (Method m:object.getClass().getDeclaredMethods()) {
if(Modifier.isPublic(m.getModifiers())&&!m.isDefault()){
method=m;
publicMethods++;
}
}
if(publicMethods==1)
{
Object returnValue = method.invoke(object, param);
return returnValue;
}
throw new InvalidParameterException("Object of type"+ object.getClass()+" is not callable!");
}
public double m2(String s, int n){return n;}
public interface InterfaceWithDefault{
public void print();
default void defaultBar() {System.out.println("x");}
}
}
public class Main {
public static void main(String[] args) throws Exception {
Example exa = new Example();
}
}
r called
c called
Hello World
print
null
1000
null
123.0
null