在 Nashorn 與 Java API(一) 中介紹了一些如何撰寫 JavaScript 來使用 Java API,語言本身並不是一對一,因而若要進一步使用 Java 的一些特性,就得有一些額外的對應方式。
指定重載方法
實際上方法在 JavaScript 不過是個物件上的特性,為函式實例,因此,除了使用 . 來存在方法之外,也可以使用 [] 來取得代表方法的函式:
var System = Java.type('java.lang.System');
System.out.println('Hello, World');    // Hello, World
System.out['println']('Hello, World'); // Hello, World
Java 中支援重載(Overload)方法,例如,System.out 的 println 有多個重載版本,如果你想指定呼叫特定的重載版本,可以使用 [] 時指定參數列。例如:
var System = Java.type('java.lang.System');
System.out['println'](3.14);          // 3.14
System.out['println(double)'](3.14);  // 3.14
System.out['println(int)'](3.14);     // 3
實例方法也可以使用這種方式:
var PrintStream = Java.type('java.io.PrintStream');
var file = new PrintStream('test.txt');
file['println(int)'](3.14);  // test.txt 中只存 3 這個數值
file.close();
JavaImporter、importPackage、 importClass
Java 中有 import 語法,在 Nashorn 中可以用 Java.type 來模擬 import 特定類別的作用:
var PrintStream = Java.type('java.io.PrintStream');
如果想同時將數個類別或套件放在某個名稱空間下,可以使用 JavaImport。例如:
var commons = new JavaImporter(java.util, java.lang);
with(commons) {
    var lt = Arrays.asList(1, 2, 3);
    System.out.println(lt);   // [1, 2, 3]
}
Nashorn 內建了 mozilla_compat.js,可以使用 load 載入後,使用 imoprtPackage 或 importClass,前者相當於在 Java 中 import 時在類別的部份使用 *,後者用來 import 某個類別。例如:
load('nashorn:mozilla_compat.js');
importClass(java.lang.System);
importPackage(java.util);
System.out.println('Hello, World'); // Hello, World
var lt = Arrays.asList(1, 2, 3);
System.out.println(lt);             // [1, 2, 3]
實作介面
如果想要實作介面,可以使用 Java.extend。例如:
load('nashorn:mozilla_compat.js');
importPackage(java.lang);
var RunDemo = Java.extend(Runnable, {
    run: function() {
        [1, 2, 3].forEach(print);
    }
});
var th = new Thread(new RunDemo());
th.start();
th.join();
不使用 Java.extend,也可以用類似 Java 的匿名類別實作語法:
load('nashorn:mozilla_compat.js');
importPackage(java.lang);
var r = new Runnable {
    run: function() {
        [1, 2, 3].forEach(print);
    }
};
var th = new Thread(r);
th.start();
th.join();
如果方法的參數型態是個只具備單一抽象方法的介面,可以直接使用函式實作。例如:
var IntStream = Java.type("java.util.stream.IntStream");
var sum = IntStream.of(1, 2, 3, 4, 5, 6)
                   .filter(function(elem) {
                       return elem > 2;
                   })
                   .sum();
print(sum); // 18
繼承類別
如果是要繼承抽象類別並實作抽象方法,方式與實作介面是類似的:
load('nashorn:mozilla_compat.js');
importPackage(java.util);
var r = new TimerTask {
    run: function() {
        [1, 2, 3].forEach(print);
    }
};
r.run();
var DemoTask = Java.extend(TimerTask, {
    run: function() {
        [1, 2, 3].forEach(print);
    }
});
new DemoTask().run();
然而,如果是繼承非抽象類別,就必須使用 java.extend:
load('nashorn:mozilla_compat.js');
importPackage(java.lang);
var DemoThread1 = Java.extend(Thread, {
    run: function() {
        [1, 2, 3].forEach(print);
    }
});
new DemoThread1().run();
var DemoThread2 = Java.extend(Thread);
(new DemoThread2 {
    run: function() {
        [1, 2, 3].forEach(print);
    }
}).run();
實際上,Java.extend 每次呼叫後,都會建立一個子類別的型態物件,因此,以下兩種寫法有些不同:
load('nashorn:mozilla_compat.js');
importPackage(java.lang);
var DemoThread1 = Java.extend(Thread, {
    run: function() {
        [1, 2, 3].forEach(print);
    }
});
var DemoThread2 = Java.extend(Thread, {
    run: function() {
        [1, 2, 3].forEach(print);
    }
});
System.out.println(new DemoThread1().getClass() === new DemoThread2().getClass()); // false
var DemoThread = Java.extend(Thread);
var th1 = new DemoThread {
    run: function() {
        [1, 2, 3].forEach(print);
    }
};
var th2 = new DemoThread {
    run: function() {
        [1, 2, 3].forEach(print);
    }
};
System.out.println(th1.getClass() === th2.getClass());  // true
呼叫父類別方法
如果想呼叫父類別建構式並同時重新實作某方法,可以如下:
load('nashorn:mozilla_compat.js');
importPackage(java.lang);
var Th = Java.extend(Thread);
var r = function() { 
    [1, 2, 3].forEach(function(elem) {
        print(elem);
    });
};
var th1 = new Th(r) {
    run : function() {
        Java.super(th1).run();
        [4, 5, 6].forEach(function(elem) {
            print(elem);
        });
    }
};
th1.start();
th1.join();
上面的範例也示範了 Java.super 的使用,它可用來呼叫父類別的方法。
從 Java 中執行 JavaScript
要在 Java 中使用 Nashorn 執行 JavaScript 的話,可以如下取得 Nashorn 引擎:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("nashorn");
engine.eval("print('Hello, World!')");
engine.eval(new FileReader("C:\\workspace\\hello.js"));
這是 JSR 223: Scripting for the Java Platform 規範中的東西,你可以在 Oracle 的 Scripting for the Java Platform 找到相關資訊。
實際上,這兩篇〈Nashorn 與 Java API〉的內容,在 Oracle Java Platform, Standard Edition Java Scripting Programmer's Guide 中 3 Using Java From Scripts 就有談到,如果想要瞭解更多如何在 Java 中呼叫 JavaScript,可以進一步參考 2 The Java Scripting API。
專門說明 Nashorn 的 Oracle 文件,則可以在 Java Platform, Standard Edition Nashorn User's Guide 找到。

