Nashorn 扩展
访问 Java 类
Section titled “访问 Java 类”要从 JavaScript 访问原始类型和引用类型的 Java 类型,可以调用 Java.type() 函数,返回与传入的类名称对应的类型对象。例如,以下示例演示了如何获取各种类型对象:
var ArrayList = Java.type("java.util.ArrayList");var intType = Java.type("int");var StringArrayType = Java.type("java.lang.String[]");var int2DArrayType = Java.type("int[][]");返回的类型对象可以在 JavaScript 代码中像使用 Java 类名一样使用。例如,可以使用它来实例化新对象:
var anArrayList = new Java.type("java.util.ArrayList");可以使用 Java.type() 返回的类型对象来访问静态字段和方法:
var File = Java.type("java.io.File");File.createTempFile("nashorn", ".tmp");要访问静态内部类,可以在 Java.type() 方法的参数中使用美元符号($)。以下示例演示了如何返回 java.awt.geom.Arc2D 中的 Float 内部类的类型对象:
var Float = Java.type("java.awt.geom.Arc2D$Float");如果已经有外部类的类型对象,可以像访问外部类的属性一样访问内部类:
var Arc2D = Java.type("java.awt.geom.Arc2D");var Float = Arc2D.Float;对于非静态内部类,必须将外部类的实例作为构造函数的第一个参数。
尽管 JavaScript 中的类型对象与 Java 中的 java.lang.Class 对象类似,但它们是不同的。你可以使用 class 和 static 属性将两者互相转换。
导入 Java 包和类
Section titled “导入 Java 包和类”为了通过简化名称访问 Java 类,可以使用 importPackage() 和 importClass() 函数来导入 Java 包和类。以下示例展示了如何使用这两个函数:
// 加载兼容性脚本load("nashorn:mozilla_compat.js");// 导入 java.awt 包importPackage(java.awt);// 导入 java.awt.Frame 类importClass(java.awt.Frame);// 创建新的 Frame 对象var frame = new java.awt.Frame("hello");// 调用 setVisible() 方法frame.setVisible(true);// 访问 JavaBean 属性print(frame.title);标准的 Java SE 包有快捷方式(例如,java 代表 Packages.java,javax 代表 Packages.javax,org 代表 Packages.org)。java.lang 包不会默认导入,因为其类可能与 JavaScript 中的内建对象(如 Object、Boolean、Math 等)冲突。
使用 Java 数组
Section titled “使用 Java 数组”要创建一个 Java 数组对象,首先需要获取 Java 数组的类型对象,然后实例化它。访问数组元素和 length 属性的语法与 Java 中相同。以下示例演示了如何创建一个 Java 数组对象并访问其元素:
var StringArray = Java.type("java.lang.String[]");var a = new StringArray(5);
// 设置第一个元素的值a[0] = "Scripting is great!";// 打印数组的长度print(a.length);// 打印第一个元素的值print(a[0]);给定一个 JavaScript 数组,可以使用 Java.to() 方法将其转换为 Java 数组。你必须将 JavaScript 数组变量传递给此方法,并指定要返回的数组类型。
// 创建 JavaScript 数组var anArray = [1, "13", false];
// 将 JavaScript 数组转换为 Java int[] 数组var javaIntArray = Java.to(anArray, "int[]");print(javaIntArray[0]); // 输出数字 1实现 Java 接口
Section titled “实现 Java 接口”在 JavaScript 中实现 Java 接口的语法类似于在 Java 中声明匿名类。你可以实例化一个接口,并在同一个表达式中实现其方法。以下示例演示了如何实现 Runnable 接口:
// 创建一个实现 Runnable 接口的对象,run 方法作为 JavaScript 函数实现var r = new java.lang.Runnable() { run: function() { print("running...\n"); }};
// 将 r 变量传递给期望实现了 java.lang.Runnable 接口的 Java 方法var th = new java.lang.Thread(r);th.start();th.join();如果方法期望一个只实现单一方法的接口对象,可以直接传递一个 JavaScript 函数,而不必创建完整的实现对象。
扩展抽象 Java 类
Section titled “扩展抽象 Java 类”你可以通过传递一个 JavaScript 对象并在其中实现抽象方法来实例化抽象 Java 类的匿名子类。以下示例展示了如何实例化 java.util.TimerTask 类的一个子类:
var TimerTask = Java.type("java.util.TimerTask");var task = new TimerTask({ run: function () { print("Hello World!"); }});扩展具体 Java 类
Section titled “扩展具体 Java 类”对于具体的 Java 类,不能直接使用类似于抽象类的扩展语法。要扩展具体类,必须使用 Java.extend() 函数。以下示例演示了如何扩展 java.lang.Thread 类并实现 run() 方法:
var Thread = Java.type("java.lang.Thread");var threadExtender = Java.extend(Thread);var t = new threadExtender() { run: function() { print("Thread running!") }};选择方法重载版本
Section titled “选择方法重载版本”Java 方法可以通过参数类型进行重载。在调用时,Nashorn 会自动根据实际参数类型选择正确的重载版本。如果遇到模糊的情况,可以显式选择某个特定的重载版本。
var out = java.lang.System.out;out["println(Object)"]("hello");print(__FILE__, __LINE__, __DIR__);在 JavaScript 中加载额外的脚本文件非常方便。我们可以使用 load 函数加载本地或远程脚本。
load("https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js");外部脚本会在相同 JavaScript 上下文中被执行,所以我们可以直接访问 underscore 的对象。要记住当变量名称互相冲突时,脚本的加载可能会使你的代码崩溃。
这一问题可以通过把脚本文件加载到新的全局上下文来绕过:
loadWithNewGlobal("script.js");数据类型映射
Section titled “数据类型映射”大多数 Java 与 JavaScript 之间的转换按预期进行。例如,JavaScript 数组会自动转换为 Java 数组类型,JavaScript 函数会自动转换为 SAM 类型。当数字传递给 Java API 时,它们会根据目标类型转换为期望的数字类型(无论是装箱类型还是基本类型)。
将 JSON 对象传递给 Java
Section titled “将 JSON 对象传递给 Java”Java.asJSONCompatible(obj) 函数接受一个脚本对象并返回一个与大多数 Java JSON 库兼容的对象。它将所有数组作为 List 对象暴露,而其他对象则作为 Map 对象暴露。
Nashorn 语法扩展
Section titled “Nashorn 语法扩展”条件捕获子句
Section titled “条件捕获子句”一个 try..catch 语句可以有多个 catch 子句,每个子句都有自己的捕获条件。
- 条件捕获子句示例:
try { func()} catch (e if e instanceof TypeError) { // 处理 TypeError} catch (e) { // 处理其他错误}函数表达式闭包
Section titled “函数表达式闭包”该语法允许在定义简单单行函数时省略大括号和 return 关键字。详情见 MDN 1.8 新功能。
- 闭包函数表达式示例:
function sqr(x) x*x
// 等效于// function sqr(x) { return x*x }for each 表达式
Section titled “for each 表达式”ECMAScript 的 for..in 遍历对象的属性名或数组的索引,而 for..each..in 循环遍历对象的属性值,而不是属性名或索引。详情见 MDN 参考。
for each循环示例:
// 遍历对象的每个值var arr = [ "hello", "world" ];for each (a in arr) { print(a)}for each 也适用于 Java 数组以及任何 Java 的 Iterable 对象。
Java 数组的 for each 示例
Section titled “Java 数组的 for each 示例”var JArray = Java.type("int[]");var arr = new JArray(10);for (i in arr) { arr[i] = i*i;}for each (i in arr) { print(i);}遍历 Java Map 示例
Section titled “遍历 Java Map 示例”var System = Java.type("java.lang.System")for each (p in System.properties.entrySet()) { print(p.key, "=", p.value)}
for each (e in System.env.entrySet()) { print(e.key, "=", e.value)}新表达式中的最后一个参数在 ")" 后指定
Section titled “新表达式中的最后一个参数在 ")" 后指定”在一个 new 表达式中,如果最后一个参数是对象字面量,可以在 ")" 后指定该参数。
- 匿名类样式的表达式示例:
var r = new java.lang.Runnable() { run: function() { print("run"); }}匿名函数语句
Section titled “匿名函数语句”顶级函数语句可以是匿名的。
- 匿名函数语句示例:
function () { print("hello")}如果通过 eval 或脚本引擎的 eval 调用执行该代码,返回的将是函数对象,可以稍后调用。