将Java实现的函数添加到Nashorn的全局范围

时间:2015-10-22 19:33:00

标签: nashorn

我正在尝试迁移/更新我的项目以使用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实现独立功能。

2 个答案:

答案 0 :(得分:5)

您可以使用Java轻松实现脚本函数。您只需使用lambda实现任何@FunctionalInterface(manual)接口,并通过调用ScriptEngine.put(https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html)方法将其作为全局变量公开。以下示例实现了两个这样的脚本函数'用Java代码实现。

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

答案 1 :(得分:1)

在提问之后的一段时间我再次搜索了一下,发现了这篇文章:http://mail.openjdk.java.net/pipermail/nashorn-dev/2013-December/002520.html

  

*)在JDK(或您自己的)中实现任何@FunctionalInterface接口   @FunctionalInterface)和传递/放置相同的对象   javax.script.Bindings甚至是全局范围。脚本可以访问这些   虽然这些都是功能。

     

*)在你的类中实现jdk.nashorn.api.scripting.JSObject   在它上面实现“调用”方法。再次,nashorn的灵活dynalink基础   链接器将处理您的JSObject impl。好像它是一个功能。这个   也可以用来在Java中实现“构造函数”(newObject方法)   代码等等。

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

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