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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22207447/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 14:18:50  来源:igfitidea点击:

How to convert a string to a lambda expression?

javalambdajava-8

提问by skiwi

I was thinking a bit and came up with an interesting problem, suppose we have a configuration (input) file with:

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

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

And furthermore we have a list of Integers:

此外,我们有一个Integers列表:

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

Is there a way to convert the Strings (x -> x + 1, etc.) to Objects that represent a lambda expression? Which could then be used as:

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

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);
}

How would I write such a method getLambdaFromString?

我将如何编写这样的方法getLambdaFromString

  • Is there something I could reuse from the JDK/JRE?
  • Would I need to write it all by myself?
  • Is it possible to narrow down the Object lambdato something else that only captures lambdas?
  • 有什么我可以从 JDK/JRE 中重用的东西吗?
  • 需要我自己写吗?
  • 是否可以将范围缩小Object lambda到仅捕获 lambda 的其他内容?

采纳答案by Stuart Marks

Marko's comment on the question is correct. You can't read a bare Java lambda expression out of a file, since such an expression isn't defined without a target type provided by the context. For example, consider the following method declarations:

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

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

Then in the following code,

然后在下面的代码中,

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

the two lambda expressions (x, y) -> x + ymean completely differentthings. For method1, the +operator is string concatenation, but for method2, it means integer addition.

两个 lambda 表达式的(x, y) -> x + y含义完全不同。对于method1,+操作符是字符串连接,对于method2,它表示整数加法。

This is wandering a bit far afield from your question, but you can read and evaluate a lambda or function expression using a dynamic language. In Java 8 there is the Nashorn JavaScript engine. So instead of attempting to read an evaluate a Java lambda expression, you could read and evaluate a JavaScriptfunction using Nashorn, called from Java.

这离您的问题有点远,但您可以使用动态语言读取和评估 lambda 或函数表达式。在 Java 8 中有 Nashorn JavaScript 引擎。因此,您可以使用从 Java 调用的 Nashorn读取和评估JavaScript函数,而不是尝试读取 Java lambda 表达式的求值。

The following code takes a function in arg[0] and applies it to each subsequent, printing the results:

以下代码采用 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]));
        }
    }
}

For example, running the command

例如,运行命令

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

gives the results

给出结果

52.0
70.0
142.0

The wrapping of the function string inside of new java.util.function.Functionis necessary in order to create an adapter between Nashorn's notion of a JavaScript function and Java's Function interface. (There might be a better way, but I'm not aware of one.) The cast of the return value of evalto Function<Object,Object>results in an unchecked cast warning, which is unavoidable, I think, since this is the boundary between JavaScript, a dynamically-typed language, and Java, which is statically-typed. Finally, no error checking is done. I'm sure this will blow up in a variety of nasty ways if certain assumptions are violated, such as the first argument not actually representing a JavaScript function.

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

Still, you might find this technique useful if you have a need to evaluate expressions or functions read from a file.

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

回答by Pawe? Chor??yk

I believe that using Nashorn JavaScript engine mentioned in Stuart's answeris the best choice in most cases. However if, for some reason, it's desired to stay within the Java world I have recently created the LambdaFromStringlibrary that converts a String code to lambda at runtime.

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

When using that library the code doing what is specified in the question looks like this:

使用该库时,执行问题中指定的代码如下所示:

    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

The only thing that differs is that the type of lambda has to be known and passed to the library so that the compiler knows what "+" means in this context.

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