添加 Java-implemented 函数到 Nashorn 的全局范围

Add Java-implemented function into Nashorn's global scope

我正在尝试 migrate/update 我的项目使用 Rhino 的 Nashorn。我在 Java 中实现了一些全局实用函数并添加到目标脚本引擎的全局范围内,典型示例是 log(message).

在 Rhino 中,它是通过

实现的
public static class LogFunction extends org.mozilla.javascript.BaseFunction {
    @Override
    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        ...
    }
}

其实例已添加到目标范围。在 Nashorn 的情况下必须做什么?我找不到如何为 Nashorn 实现独立功能。

您可以轻松实现Java中的脚本功能。您只需实现任何 @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-) 方法。以下示例实现了两个在 Java 代码中实现的此类脚本 'functions'。

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

问完这个问题一段时间后,我再次用谷歌搜索并找到了这个 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.

我决定使用 JSObject 实现,与 Sundararajan 的回答中推荐的方法相比,我的代码看起来更像 Rhino,也更接近我的原始代码。不确定它们之间是否有任何性能差异。

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