Add Java-implemented function into Nashorn's global scope

旧巷老猫 提交于 2019-12-06 00:48:50

You can easily implement script functions in Java. You just implement any @FunctionalInterface (https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html) interface using a lambda and expose the same as a global variable by calling ScriptEngine.put (https://docs.oracle.com/javase/8/docs/api/javax/script/ScriptEngine.html#put-java.lang.String-java.lang.Object-) method. The following example implements two such script 'functions' implemented in Java code.

import javax.script.*;
import java.util.function.*;
import java.util.Random;

public class Main {
  public static void main(String[] args) throws Exception {
     ScriptEngineManager m = new ScriptEngineManager();
     ScriptEngine e = m.getEngineByName("nashorn");

     // expose 'log' function - any @FunctionInterface Java
     // object can be exposed as 'function'
     e.put("log", (Consumer<String>)System.out::println);

     Random r = new Random();
     // expose 'next gaussian' as script global function
     e.put("gaussian", (Supplier<Double>)r::nextGaussian);

     // call functions implemented in Java!
     e.eval("log('hello')");
     e.eval("print(gaussian())");
     e.eval("print(gaussian())");
  }
}

Some time after asking the question I googled once again and found this post: http://mail.openjdk.java.net/pipermail/nashorn-dev/2013-December/002520.html

*) Implement any @FunctionalInterface interface in JDK (or your own @FunctionalInterface) and pass/put object of the same in a javax.script.Bindings or even global scope. Script can access these as though these are functions.

*) Implement jdk.nashorn.api.scripting.JSObject in your class and implement "call" method on it. Again, nashorn's flexible dynalink based linker will treat your JSObject impl. as though it is a function. This can also be used to implement "constructor" (newObject method) in Java code and so on.

I decided to go with JSObject implementation and my code looks more Rhino-like and closer to my original code than approach recommended in Sundararajan's answer. Not sure if there any performance difference between them.

import jdk.nashorn.api.scripting.AbstractJSObject;

public static class PrintFunction extends AbstractJSObject {

    public PrintFunction() {
    }

    @Override
    public boolean isFunction() {
        return true;
    }

    @Override
    public Object call(Object thiz, Object... args) {
        ... do something ...

        return null;
    }
}

...

void onInitScriptObjects(Bindings scope) {
    scope.put("print", new PrintFunction());
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!