Reputation: 662
When using java's built-in javascript interpreter, why can I iterate over a java List using Arrays.forEach() but not over a native array? I have the following test code
var ArrayList = Java.type('java.util.ArrayList');
var list = new ArrayList();
list.add('a');
list.add('b');
list.add('c');
var StringArray = Java.type("java.lang.String[]");
var array = new StringArray(3);
array[0] = "A";
array[1] = "B";
array[2] = "C";
list.forEach(function(v) { print(v); });
array.forEach(function(v) { print(v); });
which I'd expect to print out
a b c A B C
but instead I get
a b c
TypeError: [Ljava.lang.String;@644e4fbf has no such function "forEach" in at line number 14
The following works, but why not array.forEach() ?
for (var i=0; i<array.length; ++i)
print(array[i]);
The issue I have is that my javascript code wants to call a load of java functions that return a String[], and I want to deal with the resulting object as if was a regular javascript array. Is there an easier way to fix this than writing loads of wrapper functions in java that convert my arrays to an ArrayList?
Upvotes: 4
Views: 7508
Reputation: 662
Thanks for the suggestions. It's not just about iterating over the array but also that people writing scripts might expect to call functions like Arrays.sort(), Arrays.filter() etc.
In the end I decided to change all the Java functions so they return a true JavaScript array, rather than a native Java array, by passing them through a helper function to call Java.from():
private JSObject toJavascript(Object javaObject)
{
String tmpkey = "tmp"+javaObject.hashCode()+System.currentTimeMillis();
engine.put(tmpkey, javaObject);
JSObject jsObject = (JSObject)engine.eval("Java.from("+tmpkey+")");
engine.put(tmpkey, null);
return jsObject;
}
There's probably a neater to do this but at least it seems to work!
Upvotes: 0
Reputation: 4405
When you call forEach on a Java List, you are invoking the List's forEach method inherited from Iterable. Nashorn supports passing a script function whenever a @FunctionalInterface
object is expected and so you can pass function as argument for the Consumer parameter. There is no such forEach Java method on Java arrays and hence the second forEach method call fails.
Note that the Nashorn implementation of the JavaScript Array.prototype.forEach is generic. It works on Java arrays, lists as well. I adjusted your script to use Array.prototype.forEach for both Java List and java String array.
var ArrayList = Java.type('java.util.ArrayList');
var list = new ArrayList();
list.add('a');
list.add('b');
list.add('c');
var StringArray = Java.type("java.lang.String[]");
var array = new StringArray(3);
array[0] = "A";
array[1] = "B";
array[2] = "C";
var forEach = Array.prototype.forEach;
forEach.call(list, function(v) { print(v); });
forEach.call(array, function(v) { print(v); });
Upvotes: 8