如何在不使用 "internal" 子包中的 类 的情况下包装到 Nashorn 中的 NativeArray

How to wrap to NativeArray in Nashorn without using classes from "internal" subpackage

我有一个Java数组,比如说Object[],我需要传给JS执行环境,也就是ScriptEngine.

我不能简单地把它作为 属性 以下列方式表示:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
Object[] array = {1, 2, 3};
engine.put("prop", array);

因为在 JS 环境中表达式 Array.isArray(prop) 将被评估为 false,而我需要它是 true.

jdk.nashorn.internal.objects.NativeArray 构造函数是封闭的,这意味着您不能显式实例化 NativeArray

然而,可以使用 jdk.nashorn.internal.objects.Global.instance().wrapAsObject 将 Java 数组 Object[] 转换为 NativeArray,结果对象将被识别为 JS 数组,即 Array.isArray 对于此对象 return 为真。

虽然这给出了预期的结果,但使用 internal 包中的 类 并不是一个好主意,如果你使用 Java 9.[=26 则更糟=]

因此,我想知道,假设我无法更改 JS 源,是否有更好的方法向 JS 执行环境提供 Java 对象,以便该对象被识别为真正的 JS 数组,即 Array.isArray return是真的吗?

可以通过javascript创建一个原生数组,然后将对应的ScriptObjectMirror转换为一个List<Object>,看起来列表会使用原生数组作为底层存储:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");     
ScriptObjectMirror jsArray = (ScriptObjectMirror) engine.eval("var arr = []; arr");

@SuppressWarnings("unchecked")
List<Object> ls = jsArray.to(List.class);
ls.add(1);
ls.add(2);
ls.add(3);

System.out.println(ls); // [1, 2, 3]
engine.eval("print(arr)"); // 1,2,3
engine.eval("print(Array.isArray(arr))"); // true

然后您可以在 Java 端使用此列表。

Nashorn 有一个非标准的顶级 Java 对象,其中包含名为 tofrom 的有用转换方法。如果你有一个 Java 数组 arr,那么 Java.from(arr) 将创建一个 JS 数组作为 Java 数组的浅表副本。它也适用于任何 java.util.Collection.

请注意,在大多数情况下,您可以在 Nashorn 中原生使用 Java 数组和列表;如果出于某种原因您需要一个实际的 JavaScript 本机数组(例如,使用数组推导函数),您将需要使用此方法。