如何将字符串转换为 lambda 表达式?

2022-09-01 05:17:24

我思考了一下,想出了一个有趣的问题,假设我们有一个配置(输入)文件:

x -> x + 1
x -> x * 2
x -> x * x
x -> -x

此外,我们还有一个列表:Integer

List<Integer> list = new ArrayList<>();
list.addAll(Arrays.toList(1, 2, 3, 4, 5));

有没有办法将 s (等) 转换为表示 lambda 表达式的 s?然后可以用作:Stringx -> x + 1Object

Object lambda = getLambdaFromString("x -> x + 1");
if (lambda.getClass().equals(IntFunction.class) {
    list.stream().forEach()
        .mapToInt(x -> x)
        .map(x -> ((IntFunction)lambda).applyAsInt(x))
        .forEach(System.out::println);
}

我该如何写这样的方法?getLambdaFromString

  • 有没有可以从 JDK/JRE 重用的东西?
  • 我需要自己写吗?
  • 是否有可能将 范围缩小到仅捕获 lambda 的其他内容?Object lambda

答案 1

马尔科对这个问题的评论是正确的。您无法从文件中读取裸 Java lambda 表达式,因为如果没有上下文提供的目标类型,则不会定义此类表达式。例如,请考虑以下方法声明:

void method1(BiFunction<String,String,String> f) { ... }
void method2(BiFunction<Integer,Integer,Integer> f) { ... }

然后在下面的代码中,

method1((x, y) -> x + y);
method2((x, y) -> x + y);

这两个 lambda 表达式的含义完全不同。对于方法 1,运算符是字符串串联,但对于方法 2,它表示整数加法。(x, y) -> x + y+

这离你的问题有点远,但你可以使用动态语言阅读和计算 lambda 或函数表达式。在Java 8中有Nashorn JavaScript引擎。因此,与其尝试读取计算 Java lambda 表达式,不如使用从 Java 调用的 Nashorn 读取和计算 JavaScript 函数。

下面的代码在 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]));
        }
    }
}

例如,运行命令

java ScriptFunction 'function(x) 3 * x + 1' 17 23 47

给出结果

52.0
70.0
142.0

为了在 Nashorn 的 JavaScript 函数概念和 Java 的函数接口之间创建一个适配器,必须将函数字符串包装在 里面。(可能有更好的方法,但我不知道有。返回值 to 的强制转换会导致未经检查的强制转换警告,我认为这是不可避免的,因为这是 JavaScript(一种动态类型语言)和 Java(静态类型化语言)之间的边界。最后,不执行错误检查。我敢肯定,如果违反了某些假设,这将以各种令人讨厌的方式爆炸,例如第一个参数实际上并不代表JavaScript函数。new java.util.function.FunctionevalFunction<Object,Object>

不过,如果您需要计算从文件中读取的表达式或函数,则可能会发现此技术很有用。


答案 2

我相信使用Stuart答案中提到的Nashorn JavaScript引擎在大多数情况下是最好的选择。但是,如果出于某种原因,希望留在Java世界中,我最近创建了LambdaFromString库,该库可在运行时将字符串代码转换为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

唯一不同的是,lambda 的类型必须是已知的,并传递给库,以便编译器知道“+”在此上下文中的含义。


推荐