Java 如何将字符串转换为lambda表达式?
我考虑了一下,提出了一个有趣的问题,假设我们有一个配置(输入)文件,其中包含:Java 如何将字符串转换为lambda表达式?,java,lambda,java-8,Java,Lambda,Java 8,我考虑了一下,提出了一个有趣的问题,假设我们有一个配置(输入)文件,其中包含: x -> x + 1 x -> x * 2 x -> x * x x -> -x 此外,我们还有一个Integers列表: List<Integer> list = new ArrayList<>(); list.addAll(Arrays.toList(1, 2, 3, 4, 5)); 如何编写这样一个方法getLambdaFromString 我是否可以从JD
x -> x + 1
x -> x * 2
x -> x * x
x -> -x
此外,我们还有一个Integer
s列表:
List<Integer> list = new ArrayList<>();
list.addAll(Arrays.toList(1, 2, 3, 4, 5));
如何编写这样一个方法getLambdaFromString
- 我是否可以从JDK/JRE中重用一些东西
- 我需要自己写吗
- 是否可以将对象lambda缩小到仅捕获lambda的其他对象
- 马尔科对这个问题的评论是正确的。您无法从文件中读取裸Java lambda表达式,因为如果没有上下文提供的目标类型,则不会定义此类表达式。例如,考虑下面的方法声明:
void method1(BiFunction<String,String,String> f) { ... }
void method2(BiFunction<Integer,Integer,Integer> f) { ... }
这两个lambda表达式(x,y)->x+y
表示完全不同的事物。对于method1,+
运算符是字符串串联,但对于method2,它意味着整数相加
这离您的问题有点远,但您可以使用动态语言读取和计算lambda或函数表达式。在Java8中有Nashorn JavaScript引擎。因此,您可以使用从Java调用的Nashorn来读取和计算JavaScript函数,而不是尝试读取和计算Java lambda表达式
以下代码接受arg[0]中的函数,并将其应用于每个后续函数,打印结果:
import java.util.function.Function;
import javax.script.*;
public class ScriptFunction {
public static void main(String[] args) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
@SuppressWarnings("unchecked")
Function<Object,Object> f = (Function<Object,Object>)engine.eval(
String.format("new java.util.function.Function(%s)", args[0]));
for (int i = 1; i < args.length; i++) {
System.out.println(f.apply(args[i]));
}
}
}
给出了结果
52.0
70.0
142.0
为了在Nashorn的JavaScript函数概念和java函数接口之间创建一个适配器,有必要将函数字符串包装在new java.util.function.function
中。(可能有更好的方法,但我不知道。)将eval
的返回值强制转换为Function
会导致未经检查的强制转换警告,我认为这是不可避免的,因为这是动态类型语言JavaScript和静态类型语言Java之间的边界。最后,没有进行错误检查。我敢肯定,如果违反了某些假设,例如第一个参数实际上并不表示JavaScript函数,那么这将以各种令人讨厌的方式爆发
不过,如果您需要计算从文件读取的表达式或函数,您可能会发现此技术很有用。我相信在大多数情况下,使用中提到的Nashorn JavaScript引擎是最佳选择。但是,如果出于某种原因,希望留在Java世界中,我最近创建了一个库,它在运行时将字符串代码转换为lambda 使用该库时,执行问题中指定操作的代码如下所示:
List<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3, 4, 5));
LambdaFactory lambdaFactory = LambdaFactory.get();
Function<Integer, Integer> lambda = lambdaFactory
.createLambda("x -> x + 1", new TypeReference<Function<Integer, Integer>>() {});
list.stream().map(lambda).forEach(System.out::println); //prints 2 to 6
List List=new ArrayList();
addAll(Arrays.asList(1,2,3,4,5));
lambdfactory lambdfactory=lambdfactory.get();
函数lambda=lambdfactory
.createLambda(“x->x+1”,新类型引用(){});
list.stream().map(lambda.forEach(System.out::println)//打印2到6
唯一不同的是必须知道lambda的类型并将其传递给库,以便编译器知道“+”在该上下文中的含义 您可以使用一个公共静态方法创建一个类,该方法返回lambda并动态编译它。或者对于此类简单表达式,使用脚本引擎计算箭头后的表达式。lambda表达式计算为单个方法接口的实现。哪个接口取决于词汇上下文。因此,您不能期望重用任何功能(来自JDK或其他)来将字符串从上下文解析到适当的实例中。您的问题是如何将表达式字符串解析为可执行代码段这一非常古老的问题。您刚刚用术语“lambda表达式”替换了“可执行代码段”。这看起来真的很有趣,我应该更深入地探索Nashorn引擎!我有一种感觉,在运行时使用Nashorn引擎比在运行时动态创建java文件更容易。非常好的Stuart。您是否知道过滤器谓词是否也可以用nashorn引擎解析?Say.filter(u->u.key.equals(“A”)?刚刚意识到我只需要使用谓词而不是函数。谢谢
java ScriptFunction 'function(x) 3 * x + 1' 17 23 47
52.0
70.0
142.0
List<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3, 4, 5));
LambdaFactory lambdaFactory = LambdaFactory.get();
Function<Integer, Integer> lambda = lambdaFactory
.createLambda("x -> x + 1", new TypeReference<Function<Integer, Integer>>() {});
list.stream().map(lambda).forEach(System.out::println); //prints 2 to 6