Java 在Nashorn之间无缝地传递数组和列表
我知道您可以在Nashorn中使用Java数组,并且有很多例子说明了如何做到这一点。对于我来说,标准方法的问题在于它使javascript代码明确地知道它的运行时环境。目前我有一个利用Rhino的解决方案,它可以在Java类型和本机javascript类型之间无缝转换 对于Rhino,我通过实现Java 在Nashorn之间无缝地传递数组和列表,java,arrays,nashorn,Java,Arrays,Nashorn,我知道您可以在Nashorn中使用Java数组,并且有很多例子说明了如何做到这一点。对于我来说,标准方法的问题在于它使javascript代码明确地知道它的运行时环境。目前我有一个利用Rhino的解决方案,它可以在Java类型和本机javascript类型之间无缝转换 对于Rhino,我通过实现org.mozilla.javascript.ContextFactory和org.mozilla.javascript.WrapFActory并在调用makeContext时在Context上设置Wra
org.mozilla.javascript.ContextFactory
和org.mozilla.javascript.WrapFActory
并在调用makeContext
时在Context
上设置WrapFActory来实现这一点。此WrapFactory实现负责Java数组和列表与本机javascript数组和列表之间的转换。它还愤怒地提到,我必须从JDK获得Rhino源代码才能使这种方法发挥作用
我需要为纳肖恩找到一个类似的解决方案。下面是一个我正在努力完成的例子
public static void main(String args[]) {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine();
try {
engine.eval("function print_array(arr) { print(arr); }");
engine.eval("function print_native() { print_array([1, 2, 3, 4]); }");
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("print_array", new int[]{1, 2, 3, 4});
invocable.invokeFunction("print_array", Arrays.asList(1, 2, 3, 4));
invocable.invokeFunction("print_native");
} catch (ScriptException | NoSuchMethodException e) {
e.printStackTrace();
}
}
这段代码的输出是
[I@169e6180
[1,2,3,4]
1,2,3,4
我正在寻找一种实现ScriptObjectMirror的方法,假设这是正确的,这将使这三个invokeFunction
调用的输出相同
我尝试在ScriptUtils
上使用wrap
函数,但结果仍然是错误的
更新
我试图创建一个类型为Invocable
的动态代理,并在InvocationHandler
中进行转换。要使用Nashorn创建NativeArray,似乎应该使用jdk.Nashorn.internal.objects.Global.allocate
,但这总是会引发异常
Global.allocate(new int[] {1, 2, 3, 4})
提高
Exception in thread "main" java.lang.NullPointerException
at jdk.nashorn.internal.objects.Global.instance(Global.java:491)
at jdk.nashorn.internal.objects.NativeArray.<init>(NativeArray.java:141)
at jdk.nashorn.internal.objects.Global.allocate(Global.java:1584)
线程“main”java.lang.NullPointerException中的异常
位于jdk.nashorn.internal.objects.Global.instance(Global.java:491)
位于jdk.nashorn.internal.objects.NativeArray。(NativeArray.java:141)
位于jdk.nashorn.internal.objects.Global.allocate(Global.java:1584)
我认为实现AbstractJSObject需要付出艰苦的努力。我认为很多像getMember这样的函数都可以通过Refelction来实现。但是如果有人认为它是一个JS数组并尝试扩展原型,你会怎么做呢?你也想处理这个问题吗?在这种情况下,我会将JS数组作为属性实现在一个列表中,如包装类并将所有集合/添加委托给更新JS对象的JS函数
解决方案1:
public static void main(String args[]) {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine();
try {
engine.eval("function print_array(arr) { print(arr); for(var i=0; i<arr.length; i++) {print(arr[i]);}}");
engine.eval("function print_native() { print_array([1, 2, 3, 4]); }");
engine.eval("function get_native() { return [1, 2, 3, 4]; }");
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("print_array", new int[]{1, 2, 3, 4});
invocable.invokeFunction("print_array", Arrays.asList(1, 2, 3, 4));
invocable.invokeFunction("print_array", new Foo());
invocable.invokeFunction("print_native");
ScriptObjectMirror a = (ScriptObjectMirror) invocable.invokeFunction("get_native");
System.out.println(invocable.invokeFunction("get_native"));
} catch (Exception e) {
e.printStackTrace();
}
}
static class Foo extends AbstractJSObject {
Map<Integer, Object> arrayValues = new HashMap<>();
public Foo() {
arrayValues.put(0, 1);
arrayValues.put(1, 2);
arrayValues.put(2, 3);
}
@Override
public Object call(Object thiz, Object... args) {
System.out.println("call");
return super.call(thiz, args);
}
@Override
public Object newObject(Object... args) {
System.out.println("new Object");
return super.newObject(args);
}
@Override
public Object eval(String s) {
System.out.println("eval");
return super.eval(s);
}
@Override
public Object getMember(String name) {
System.out.println("getMember " + name);
return name.equals("length") ? arrayValues.size() : arrayValues.get(Integer.valueOf(name));
}
@Override
public Object getSlot(int index) {
//System.out.println("getSlot");
return arrayValues.get(index);
}
@Override
public boolean hasMember(String name) {
System.out.println("hasMember");
return super.hasMember(name);
}
@Override
public boolean hasSlot(int slot) {
System.out.println("hasSlot");
return super.hasSlot(slot);
}
@Override
public void removeMember(String name) {
System.out.println("removeMember");
super.removeMember(name);
}
@Override
public void setMember(String name, Object value) {
System.out.println("setMember");
super.setMember(name, value);
}
@Override
public void setSlot(int index, Object value) {
System.out.println("setSlot");
super.setSlot(index, value);
}
@Override
public Set<String> keySet() {
System.out.println("keySet");
return arrayValues.keySet().stream().map(k -> "" + k).collect(Collectors.toSet());
}
@Override
public Collection<Object> values() {
System.out.println("values");
return arrayValues.values();
}
@Override
public boolean isInstance(Object instance) {
System.out.println("isInstance");
return super.isInstance(instance);
}
@Override
public boolean isInstanceOf(Object clazz) {
System.out.println("isINstanceOf");
return super.isInstanceOf(clazz);
}
@Override
public String getClassName() {
System.out.println("getClassName");
return super.getClassName();
}
@Override
public boolean isFunction() {
return false;
}
@Override
public boolean isStrictFunction() {
return false;
}
@Override
public double toNumber() {
return super.toNumber();
}
@Override
public boolean isArray() {
return true;
}
@Override
public String toString() {
return arrayValues.values().toString();
}
}
您的
Arrays.asList()
调用似乎使用int[]
类型的单个元素创建了一个列表,而不是您想要的。请尝试Arrays.asList(1,2,3,4)
相反。啊,谢谢:)这确实让事情变得更好了。我将更新这个问题。这里的主要问题不是JS数组实际上不是Java意义上的数组吗?它们更像是带有int
键的映射。我对JS知之甚少,无法对此发表评论,但要解决我的问题,我需要将数组转换为NativeArray
static class FooList implements List {
final ScriptObjectMirror wrapped;
public FooList(ScriptObjectMirror wrapped) {
this.wrapped = wrapped;
}
@Override
public int size() {
return engine.eval("get length of wrapped JS object");
}
... and so on ...
}