是否可以使用反射从String
创建java.util.function.Function
?我似乎无法想出一种方法来做到这一点,因为没有Function
的构造函数。
假设我有这个方法:
public Integer hello() {
return 5;
}
我想获得hello()
方法的Function
引用。我需要传递方法的名称,在本例中为"hello",并获得Function
.
我也可以用这种方法得到Function
, Test::hello
,但我想做的是Test::"hello"
@immibis提到尝试这个:
private Function<Test, Integer> getFunction(String s){
return (Test o) -> s;
} ^
但遗憾的是,当这样调用它时,getFunction("hello")
不起作用。IntelliJ IDEA给了我这个错误信息:
Bad return type in lambda expression: String cannot be converted to Integer
这是不是的副本,我不想要Method
响应,而是Function
响应。
直观地说,我们已经在我们通常编写的函数(方法)中加入了很多动态元素:我们使用各种条件分支和循环。如果你能约束可以做什么,你就可以用这些简单的结构来构建你的函数。
然而,从你的问题中并没有明确你所期望的动态:
- 实际java代码
- 计算简单表达式,如
1+2*(4-8)
- 或其他脚本,比如你想解析和计算的
对于实际的Java编码,我建议使用API/SPI对实现某种抽象。SPI是一个服务提供者接口,或者是一种抽象,它允许其他人提供现成的和编译过的类作为扩展。我相信OSGI提供了一种标准的方法。
要计算表达式,有许多第三方库可用。我开发了一个,但不会提及,因为有许多其他可用的。这个板的目的不是为了在订单上提出一个工具。你也可以考虑一下Nashorn,它是一个JavaScript引擎。
要真正允许脚本,我建议坚持使用javascript和使用Nashorn。Java允许插件,实际上允许您添加额外的脚本引擎作为JSR-223的一部分。
(更新)根据你的说明和你的例子,是的,我们需要使用某种类型的反射。
在您的例子中,您想要惰性地决定将该方法应用于哪个类或实例。这限制了我提供如下的解决方案,但是我更进一步,针对一种情况优化了实现:功能对象的实例类将被应用或可以预先确定。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
public class Play {
public int hello() {
return 5;
}
static public int byebye() {
return -1;
}
public static class ExtendedPlay extends Play {
@Override
public int hello() {
return 10;
}
}
private static <T> Function<T,Integer> getFunction(Class<T> clazz,String method) throws NoSuchMethodException {
Method m = clazz.getDeclaredMethod(method);
return (o)->{
try {
return ((Integer)m.invoke(o));
} catch (IllegalAccessException | InvocationTargetException ex) {
// Just hope and pray this will be all ok!
}
return 0;
};
}
private static <T> Function<Class<T>,Integer> getStaticFunction(Class<T> clazz,String method) throws NoSuchMethodException {
Method m = clazz.getDeclaredMethod(method);
return (o)->{
try {
return ((Integer)m.invoke(o));
} catch (IllegalAccessException | InvocationTargetException ex) {
// Just hope and pray this will be all ok!
}
return 0;
};
}
private static Function<Object,Integer> getFunction(String method) {
return (o)->{
try {
Method m;
if (o instanceof Class) // For static methods
m = ((Class)o).getDeclaredMethod(method);
else // For instance methods
m = o.getClass().getDeclaredMethod(method);
return ((Integer)m.invoke(o));
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
// Just hope and pray this will be all ok!
}
return 0;
};
}
public static void main(String args[]) throws NoSuchMethodException {
// Little quicker because class type and Method instance can be resolved before multiple executions.
// Method is cached and has better compile-time type checking, but requires extra paramter.
Function<Play,Integer> f1 = getFunction(Play.class,"hello");
Function<Class<Play>,Integer> f2 = getStaticFunction(Play.class,"byebye");
// Little slower, because method instance has to be resolved for each subsequent call
// of the dereferenced Function Object. Slower but use is simpler: one less parameter, and works for
// both static and instance methods.
Function<Object,Integer> f3 = getFunction("hello");
System.out.println("Value1 is: "+f1.apply(new ExtendedPlay()));
System.out.println("Value2 is: "+f2.apply(Play.class));
System.out.println("Value3 is: "+f3.apply(new Play()));
}
}
请注意,我以这样一种方式创建了这个解决方案,它可以同时在静态和实例方法上工作。
@JoD。的答案是正确的。这里我采用另一种方法来解决问题,不使用反射:
public class Test {
private final int number;
public Test(int number) {
this.number = number;
}
public int increment() {
return this.number + 1;
}
public int decrement() {
return this.number - 1;
}
public static void main(String[] args) {
// Define references to methods
Function<Test, Integer> incr = Test::increment;
Function<Test, Integer> decr = Test::decrement;
// Store method references in a map
Map<String, Function<Test, Integer>> map = new HashMap<>();
map.put("incr", incr);
map.put("decr", decr);
// Define invocation: based on a string, select method reference to use
Function<String, Function<Test, Integer>> invocation = k -> map.get(k);
// Now the test
Test test1 = new Test(10);
int incrOnTest1 = invocation.apply("incr").apply(test1);
int decrOnTest1 = invocation.apply("decr").apply(test1);
System.out.println(incrOnTest1); // 11
System.out.println(decrOnTest1); // 9
Test test2 = new Test(50);
int incrOnTest2 = invocation.apply("incr").apply(test2);
int decrOnTest2 = invocation.apply("decr").apply(test2);
System.out.println(incrOnTest2); // 51
System.out.println(decrOnTest2); // 49
}
}
的思想是将方法的引用声明为函数,并将其存储在map中,并使用某个字符串作为键值。然后,定义一个特殊的调用函数,它接收一个字符串并查询映射以返回相应的方法引用。最后,将返回的函数应用于所需的对象实例。