2015-07-16 3 views
3

У меня есть пользовательский функциональный интерфейсJava Nashorn магазин Функция

public interface ThingFunction { 
    Object apply(Thing thing, String... flags); 
} 

я в настоящее время есть способ их хранения для последующего использования

public class Thing { 
    private static Map<String, ThingFunction> functions = new HashMap<>(); 

    ... 

    public static void addFunction(String key, ThingFunction function) { 
     functions.put(key, function); 
    } 

    ... 

    public Object executeFunction(String key, String... flags) { 
     return functions.get(key).accept(this, flags); 
    } 

    ... 
} 

Я пытаюсь выставить эти функции через API JS (с использованием двигателя Nashorn). В принципе, я хочу, чтобы пользователь мог написать функцию javascript, такую ​​как function(thing, flags) {...}, и сохранить ее как функцию ThingFunction в карте функций.

Я знаю, что могу случае двигатель к Invocable и использовать Invocable::getInteface(Class) создать ThingFunction из яваскрипта кода

... 
engine.eval("function apply(actor, flags) {return 'There are ' + flags.length + ' arguments';}"); 
Invocable invocable = (Invocable) enging; 
ThingFunction function = invocable.getInterface(ThingFunction.class); 
function.apply(thing, "this", "is", "a", "test"); 
... 

Однако этот подход означает, что я могу иметь только один метод apply в двигателе. Есть ли способ сделать много функций и сохранить их на карте, как показано выше?

ответ

4

Nashorn позволяет передавать функцию скрипта в качестве аргумента для любого метода Java, для которого требуется объект типа интерфейса с одним абстрактным методом (SAM). Поскольку ваш ThingFunction является интерфейс SAM, вы можете сделать что-то вроде этого:

Файл: Main.java

import javax.script.*; 
import java.io.*; 

public class Main { 
    public static void main(String[] args) throws Exception { 
     ScriptEngineManager m = new ScriptEngineManager(); 
     ScriptEngine e = m.getEngineByName("nashorn"); 
     e.eval(new FileReader(args[0])); 
     Thing th = new Thing(); 
     // script evaluated is expected to 'register' func, foo 
     // call those functions from java 
     th.executeFunction("func", "arg1", "arg2"); 
     th.executeFunction("foo", "bar", "j"); 
    } 
} 

Файл: main.js

var Thing = Java.type("Thing"); 

// register two functions with Thing. 
// Nashorn auto-converts a script function to an object 
// implementing any SAM interface (ThingFunction in this case) 

Thing.addFunction("func", function(thing, args) { 
    print("in func"); 
    for each (var i in args) print(i); 
}); 

Thing.addFunction("foo", function(thing, args) { 
    print("in foo"); 
    for each (var i in args) print(i); 
}); 

компилировать и запускать, вы можете используйте следующие команды:

javac *.java 
java Main main.js 

Другой подход (который независим от Nashorn и будет работать с ol der Rhino jsr-223), должен использовать Invocable.getInterface (Object, Class) [http://docs.oracle.com/javase/7/docs/api/javax/script/Invocable.html#getInterface%28java.lang.Object,%20java.lang.Class%29]

В вашем скрипте вы должны определить несколько объектов - каждый из которых имеет свойство функции скрипта, называемое «apply». И вы можете создать один экземпляр ThingFunction поверх каждого такого объекта сценария. Оценивается скрипт будет выглядеть

var obj1 = { apply: function(thing, args) { ... } }; 
var obj2 = { apply: function(thing, args) { ....} }; 

Из кода Java, вы могли бы сделать что-то вроде:

Object obj1 = e.get("obj1"); 
Object obj2 = e.get("obj2"); 
ThingFunction tf1 = invocable.getInterface(obj1, ThingFunction.class); 
ThingFunction tf2 = invocable.getInterface(obj2, ThingFunction.class); 
+0

Это является превосходным * * ответ. Я попробую и дам вам знать, есть ли у меня проблемы. Вы порекомендовали бы один из двух способов над другим? Я склоняюсь к первому, но у меня нет опыта Нашона. –

+0

Работает как сон. Спасибо миллион :) –

+2

Если вы собираетесь использовать jdk8 + и nashorn, я бы рекомендовал первый подход. –