Nashorn 是基於 JVM 的 JavaScript 引擎,自然地,與 JVM 資源的互通性會是它的重點之一,基本上,你可以使用 JavaScript 的語法與 Nashorn 的擴充語法,存取 Java 的相關 API。
取得 Java API
如果想取得 Java 標準類別,可以直接指定套件階層來存取。例如:
jjs> java.lang
[JavaPackage java.lang]
jjs> java.lang.System
[JavaClass java.lang.System]
如果是自定義類別,可以使用 Packages
物件,假設 CLASSPATH
中(可在執行 jjs
時使用 -cp
指定)可以存取到 cc.openhome.GetStarted
,那麼可以如下取得 Java API:
jjs> Packages.cc.openhome
[JavaPackage cc.openhome]
jjs> Packages.cc.openhome.GetStarted
[JavaClass cc.openhome.GetStarted]
不過以上兩種方式都有些缺點,使用 .
來作特性存取需要成本,會有效率問題,如果你提供了錯誤的類別名稱,Nashorn 會假設你指定了套件,即使實際上不存在該套件:
jjs> java.lang.Syz
[JavaPackage java.lang.Syz]
為了避免這些問題,Nashorn 提供 Java.type
,你可以指定完整名稱(Fully quailfied name),以取得代表 Java 類別的物件:
jjs> var System = Java.type('java.lang.System')
jjs> System
[JavaClass java.lang.System]
使用 Java.type
的好處是,你可以直接取得陣列的代表類別:
jjs> Java.type('int[]')
[JavaClass [I]
jjs> Java.type('double[]')
[JavaClass [D]
jjs> Java.type('java.lang.String[]')
[JavaClass [Ljava.lang.String;]
JavaClass
在 Nashorn 實際上是一個 function
,因此取得之後,就可以用 JavaScript 的方式來操作:
jjs> var ArrayList = Java.type('java.util.ArrayList')
jjs> typeof ArrayList
function
jjs> var lt = new ArrayList()
jjs> lt.add('Justin')
true
jjs> lt.add('Monica')
true
jjs> lt.toString()
[Justin, Monica]
上頭示範了呼叫實例方法的方式,如果是 Java 的靜態方法,在 JavaScript 中就是函式上的特性:
jjs> var System = Java.type('java.lang.System')
jjs> System.currentTimeMillis()
1401330165615
jjs> System.out.println('Hello, World')
Hello, World
null
如果是存取類別中的靜態類別,可以依上述的方式彈性存取,例如:
jjs> Java.type('java.util.Map.Entry')
[JavaClass java.util.Map$Entry]
jjs> Java.type('java.util.Map$Entry')
[JavaClass java.util.Map$Entry]
jjs> Java.type('java.util.Map').Entry
[JavaClass java.util.Map$Entry]
JavaScript 基本型態與 Java API
JavaScript 的基本型態有數值、字串與布林值,因為 Nashorn 基於 JVM,因此這些型態實際上會與 Java API 有所對應,先來看實字的對應:
jjs> (1).class
class java.lang.Integer
jjs> (11111111111).class
class java.lang.Long
jjs> (1111111111111111111111).class
class java.lang.Double
jjs> (3.14).class
class java.lang.Double
jjs> 'Justin'.class
class java.lang.String
jjs> true.class
class java.lang.Boolean
可以看到,整數實字依長度不同,會分別對應至 Integer
、Long
、Double
,然而,如果你使用 JavaScript 的 Number
函式建立的數字,都是 Double
:
jjs> Number(1).class
class java.lang.Double
JavaScript 與 Java 陣列
JavaScript 中使用陣列實字建立的物件,依舊是 Array
實例,操作的特性依舊就是 JavaScript 規範中的特性:
jjs> var arr = [1, 2, 3]
jjs> arr.length
3
jjs> arr[3] = 4
4
jjs> arr
1,2,3,4
jjs> arr.constructor
function Array() { [native code] }
如果想要建立 Java 中的陣列物件,可以如下:
jjs> var IntArray = Java.type('int[]');
jjs> var arr = new IntArray(3)
jjs> arr[0] = 1
1
jjs> arr[1] = 2
2
jjs> arr[2] = 3
3
jjs> arr[3] = 4
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 3
上頭的陣列是具備型態約束的,也就是只能裝整數,試著在其中放些字串或浮點數值,猜猜你會看到什麼?
如果想將 JavaScript 陣列轉為 Java 陣列,可以使用 Java.to
:
jjs> var arr = Java.to([1, 2, 3], Java.type('int[]'))
jjs> arr[0]
1
jjs> arr[1]
2
jjs> arr[2]
3
jjs> arr[3]
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 3
Java.to([1, 2, 3], Java.type('int[]'))
可以簡單寫為 Java.to([1, 2, 3], 'int[]')
,如果想將 List
轉為 JavaScript 陣列,則使用 Java.from
:
jjs> var lt = new java.util.ArrayList();
jjs> var arr = Java.from(lt)
jjs> Object.prototype.toString.call(arr)
[object Array]
for each 語法
如果你使用 JavaScript 的 for
語法來如下存取陣列,會取得的是陣列的索引:
jjs> for(var i in ['a', 'b', 'c']) print(i)
0
1
2
Nashorn 擴充了一個 for each
語法,可以讓你直接取得陣列元素值:
jjs> for each (var elem in ['a', 'b', 'c']) print(elem)
a
b
c
這個 for each
語法,其實也可以用 JavaScript 物件、Java 陣列、Iterable
、Map
實例上。例如:
jjs> var map = new java.util.HashMap()
jjs> map.put('k1', 10)
null
jjs> map.put('k2', 20)
null
jjs> for each(var value in map) print(value)
10
20
jjs> for each(var value in {x: 10, y: 20}) print(value)
10
20
jjs>
JavaBean、List 與 Map
JavaBean 物件的 Getter、Setter,可以在 Nashorn 中使用 .
或 []
存取。例如:
jjs> var Date = Java.type('java.util.Date')
jjs> var instant = new Date()
jjs> instant.time
1401334149733
jjs> instant['time']
1401334149733
jjs> instant.time = 1401334149733 + 1000
1401334150733
jjs> instant.time
1401334150733
List
實例在 Nashorn 中,可以使用 []
指定索引來代替 get
。例如:
jjs> var ArrayList = java.util.ArrayList
jjs> var lt = new ArrayList()
jjs> lt.add(1)
true
jjs> lt.add(2)
true
jjs> lt[0]
1
jjs> lt[1]
2
Map
實例在 Nashorn 中,也可以使用 .
或 []
存取:
jjs> var HashMap = java.util.HashMap
jjs> var map = new HashMap
jjs> map['k1'] = 10
10
jjs> map.k2 = 20
20
jjs> map.get('k1')
10
jjs> map['k2']
20