Passing HashMap from Java to Java Nashorn

· klm's blog


Original post is here: eklausmeier.goip.de

Java Nashorn is the JavaScript engine shipped since Java 8. You can therefore use JavaScript wherever you have at least Java 8. Java 8 also has a standalone interpreter, called jjs.

It is possible to create a Java HashMap and use this structure directly in JavaScript. Here is the code:

 1import java.util.*;
 2import java.io.*;
 3import javax.script.*;
 4
 5
 6public class HashMapDemo {
 7
 8        public static void main(String[] args) {
 9                HashMap hm = new HashMap();
10
11                hm.put("A", new Double(3434.34));
12                hm.put("B", new Double(123.22));
13                hm.put("C", new Double(1200.34));
14                hm.put("D", new Double(99.34));
15                hm.put("E", new Double(-19.34));
16
17                for( String name: hm.keySet() )
18                        System.out.println(name + ": "+ hm.get(name));
19
20                // Increase A's balance by 1000
21                double balance = ((Double)hm.get("A")).doubleValue();
22                hm.put("A", new Double(balance + 1000));
23                System.out.println("A's new account balance : " + hm.get("A"));
24
25                // Call JavaScript from Java
26                try {
27                        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
28                        engine.eval("print('Hello World');");
29                        engine.eval(new FileReader("example.js"));
30                        Invocable invocable = (Invocable) engine;
31                        Object result = invocable.invokeFunction("sayHello", "John Doe");
32                        System.out.println(result);
33                        System.out.println(result.getClass());
34
35                        result = invocable.invokeFunction("prtHash", hm);
36                        System.out.println(result);
37                } catch (FileNotFoundException | NoSuchMethodException | ScriptException e) {
38                        e.printStackTrace();
39                        System.out.println(e);
40                }
41
42        }
43}

And here is the corresponding JavaScript file example.js:

 1var sayHello = function(name) {
 2        print('Hello, ' + name + '!');
 3        return 'hello from javascript';
 4};
 5
 6var prtHash = function(h) {
 7        print('h.A = ' + h.A);
 8        print('h.B = ' + h["B"]);
 9        print('h.C = ' + h.C);
10        print('h.D = ' + h["D"]);
11        print('h.E = ' + h.E);
12};

Output is:

 1$ java HashMapDemo
 2A: 3434.34
 3B: 123.22
 4C: 1200.34
 5D: 99.34
 6E: -19.34
 7A's new account balance : 4434.34
 8Hello World
 9Hello, John Doe!
10hello from javascript
11class java.lang.String
12h.A = 4434.34
13h.B = 123.22
14h.C = 1200.34
15h.D = 99.34
16h.E = -19.34
17null

Above example uses sample code from

  1. Riding the Nashorn: Programming JavaScript on the JVM (dead link)
  2. Simple example for Java HashMap
  3. Nashorn: Run JavaScript on the JVM

Decisive was the statement in https://winterbe.com/posts/2014/04/05/java8-nashorn-tutorial/:

Java objects can be passed without loosing any type information on the javascript side. Since the script runs natively on the JVM we can utilize the full power of the Java API or external libraries on nashorn.

Above program works the same if one changes HashMap to HashMap and populating accordingly, e.g.:

1HashMap hm = new HashMap();
2
3hm.put("A", new Double(3434.34));
4hm.put("B", new String("Test"));
5hm.put("C", new Date(5000));
6hm.put("D", new Integer(99));
7hm.put("E", new Boolean(Boolean.TRUE));

Output from JavaScript would be

1h.A = 4434.34
2h.B = Test
3h.C = Thu Jan 01 01:00:05 CET 1970
4h.D = 99
5h.E = true

Entries changed in JavaScript can be returned back to Java. Assume JavaScript program changes values:

1var prtHash = function(h,hret) {
2        hret.U = 57;
3        hret.V = "Some text";
4        hret.W = false;
5};

Then these changed arguments can be used back in Java program:

1HashMap hret = new HashMap();
2
3result = invocable.invokeFunction("prtHash", hm, hret);
4System.out.println(result);
5System.out.println("hret.U = " + hret.get("U"));
6System.out.println("hret.V = " + hret.get("V"));
7System.out.println("hret.W = " + hret.get("W"));

Output is then

1hret.U = 57
2hret.V = Some text
3hret.W = false

Added 09-Dec-2020: Since JDK 15 Nashorn is no longer available. It is now necessary to use GraalVM. Here is a Migration Guide from Nashorn to GraalVM JavaScript. See Compiling Java source to binary (native).