38

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:

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:

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?

  • 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 lambda to something else that only captures lambdas?
3
  • You could create a class with one public static method that returns the lambda and compile it on the fly. Or for such simple expressions use a script engine to evaluate the expression after the arrow. Commented Mar 5, 2014 at 19:40
  • 8
    A lambda expression evaluates to an implementation of a single-method interface. Which interface depends on the lexical context. Therefore you can't expect to reuse any functionality (from the JDK or otherwise) to parse a string out of context into the appropriate instance. Commented Mar 5, 2014 at 19:41
  • 3
    Your question is the very old question of how to parse an expression string to an executable piece of code. You just replaced “executable piece of code” by the term “lambda expression”. Commented Mar 6, 2014 at 18:17

2 Answers 2

35

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:

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 + y mean completely different things. For method1, the + operator is string concatenation, but for method2, it means integer addition.

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 JavaScript function using Nashorn, called from Java.

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

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.Function is 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 eval to 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.

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

Sign up to request clarification or add additional context in comments.

3 Comments

This looks really interesting, I should explore the Nashorn engine more! I have a feeling working on runtime with the Nashorn engine is going to be easier than dynamically creating java files on runtime.
Very nice Stuart. Do you happen to know if a filter predicate can be also parsed with nashorn engine? Say .filter(u -> u.key.equals("A")) ?
Just realized that I just need to use Predicate instead of Function. Thanks
20

I believe that using Nashorn JavaScript engine mentioned in Stuart's answer is 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 LambdaFromString library that converts a String code to lambda at runtime.

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.