问题
I'm trying to migrate/update my project to use Nashorn from Rhino. I have some global utility functions implemented in Java and added into global scope of the target script engine, typical example is log(message)
.
In Rhino it is implemented via
public static class LogFunction extends org.mozilla.javascript.BaseFunction {
@Override
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
...
}
}
whose instance added into target scope. What has to be done in case of Nashorn? I can't find how standalone function can be implemented for Nashorn.
回答1:
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())");
}
}
回答2:
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());
}
来源:https://stackoverflow.com/questions/33289217/add-java-implemented-function-into-nashorns-global-scope