Java Platform Standard Edition

Javaスクリプト・プログラマーズ・ガイド

リリース9

E90917-01(原本部品番号:E61650-02)

2017年9月

Javaアプリケーションでのスクリプト言語の使用方法と、スクリプトからJava構文にアクセスする方法について説明します。

Java Platform, Standard Edition Java Scriptingプログラマ・ガイド リリース9

E90917-01

Copyright © 2015, 2017, Oracle and/or its affiliates All rights reserved.

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

このソフトウェアもしくはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアもしくはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

OracleおよびJavaはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、それぞれの所有者の商標または登録商標です。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

目次

タイトルおよび著作権情報

はじめに

- 対象読者

- ドキュメントのアクセシビリティについて

- 関連ドキュメント

- 表記規則

1 スクリプト言語とJava

2 JavaスクリプトAPI

- JavaScriptパッケージ

- JavaスクリプトAPIを使用してスクリプトを埋め込む方法

- Javaのクラスを使用したJavaスクリプトAPIの例

3 スクリプトからのJavaの使用

- Javaクラスへのアクセス

- Javaパッケージおよびクラスのインポート

- Java配列の使用

- Javaインタフェースの実装

- 抽象Javaクラスの拡張

- 具象Javaクラスの拡張

- スーパークラスのメソッドへのアクセス

- クラスへの実装のバインド

- メソッド・オーバーロード・バリアントの選択

- データ型のマッピング

- JavaへのJSONオブジェクトの引渡し

はじめに

このドキュメントでは、Java Platform, Standard Edition (Java SE)のスクリプト機能の概要について説明します。

対象読者

このドキュメントは、スクリプト言語で記述されたコードを実行する必要があるJavaアプリケーション・プログラマを対象としています。JavaおよびECMAScript言語標準の1つのバージョン(JavaScript)に習熟していることを前提としています。

ドキュメントのアクセシビリティについて

Oracleのアクセシビリティへの取り組みについては、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)にアクセスしてください。

Oracle Supportへのアクセス

サポートをご購入のOracleのお客様は、My Oracle Supportにアクセスして電子サポートを受けることができます。詳細は、http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info、聴覚に障害があるお客様はhttp://www.oracle.com/pls/topic/lookup?ctx=acc&id=trsを参照してください。

関連ドキュメント

JavaスクリプトAPIの仕様に関する項のjavax.scriptパッケージを参照してください。

表記規則

このマニュアルでは次の表記規則を使用します。

規則

意味

太字

太字は、操作に関連するGraphical User Interface要素、または本文中で定義されている用語および用語集に記載されている用語を示します。

イタリック体

イタリックは、ユーザーが特定の値を指定するプレースホルダ変数を示します。

固定幅フォント

固定幅フォントは、段落内のコマンド、URL、サンプル内のコード、画面に表示されるテキスト、または入力するテキストを示します。

1 スクリプト言語とJava

この項では、スクリプト言語の特性とJavaプログラマによるスクリプト言語の使用方法について説明します。

スクリプト言語は、スクリプトを記述する機能をサポートするプログラミング言語です。実行する前にバイト・コードにコンパイルする必要がある他のプログラミング言語のソースファイルとは異なり、スクリプトは実行環境(この場合はスクリプト・エンジン)によって直接評価されます。

ほとんどのスクリプト言語は動的に型付けされます。このため、変数型を宣言せずに新しい変数を作成できます(インタプリタが変数に関連付けられたオブジェクトの型に基づいて型を割り当てます)。また、同じ変数を異なる型のオブジェクトで再利用できます(型変換が自動的に実行されます)。

スクリプト言語は、一般に構文が簡単であり、比較的少ないステップ数で複雑なタスクを実行できます。

通常、スクリプト言語は実行時に解釈されますが、Java仮想マシン(JVM)上で実行できるJavaバイト・コードにコンパイルすることもできます。特定の問題についてはスクリプト言語の方が高速で使いやすいため、Javaアプリケーションの開発者はスクリプト言語を選ぶこともあります。ただし、スクリプト言語でJavaアプリケーションを作成した場合、Java言語の利点(型の安全性やクラス・ライブラリへのアクセスなど)は失われます。

「Java Specification Request (JSR) 223: Javaプラットフォームのためのスクリプト」は、Javaとスクリプト言語の統合に関する問題を扱っています。これは、Javaアプリケーションにスクリプトを埋め込み、スクリプトからJavaオブジェクトにアクセスするための標準フレームワークとアプリケーション・プログラミング・インタフェース(API)を定義しています。

スクリプトをJavaコードに埋め込むことで、Javaアプリケーションをカスタマイズしたり、拡張したりできます。たとえば、構成パラメータ、ビジネス・ロジック、数式およびその他の外部部品をスクリプトとして記述できます。Javaアプリケーションの開発時にスクリプト言語を選択する必要はありません。JavaスクリプトAPI (JSR 223で定義)を使ってアプリケーションを作成すれば、ユーザーはJSR 223に準拠する任意の言語でスクリプトを作成できます。「JavaスクリプトAPI」を参照してください。

JSR 223に準拠する言語でスクリプトを作成すると、標準Javaライブラリのすべてにアクセスできます。「スクリプトからのJavaの使用」を参照してください。

2 JavaスクリプトAPI

この項では、JavaスクリプトAPIの概要を説明し、Javaアプリケーションにスクリプトを埋め込むために使用するJavaスクリプトAPI (JSR 223で定義)の使用方法について説明します。また、Javaクラスに関する例も豊富に掲載して、JavaスクリプトAPIの機能を例示します。

トピック

- JavaScriptパッケージ

- JavaスクリプトAPIを使用してスクリプトを埋め込む方法

JavaScriptパッケージ

JavaスクリプトAPIは、javax.scriptパッケージのクラスとインタフェースで構成されます。これは、ScriptEngineManagerクラスを出発点とする比較的小規模で単純なパッケージです。ScriptEngineManagerオブジェクトは、JARファイル・サービス検出メカニズムによってスクリプト・エンジンを検出し、特定のスクリプト言語で記述されたスクリプトを解釈するScriptEngineオブジェクトをインスタンス化できます。

Nashornエンジンは、Java SE Development Kit (JDK)にバンドルされているデフォルトのECMAScript (JavaScript)エンジンです。Nashornエンジンは、オラクル社がOpenJDKプロジェクト(Project Nashorn)の一環として完全にJavaで開発しました。

NashornはJavaスクリプトAPIで使用されるデフォルトのECMAScriptエンジンですが、JSR 223に準拠する任意のスクリプト・エンジンを使用することも、独自のエンジンを実装することもできます。このドキュメントでは、JSR 223に準拠するスクリプト・エンジンの実装について説明しませんが、もっとも基本的なレベルでは、javax.script.ScriptEngineおよびjavax.script.ScriptEngineFactoryインタフェースを実装する必要があります。抽象クラスjavax.script.AbstractScriptEngineは、ScriptEngineインタフェースのいくつかのメソッドに有用なデフォルトを提供します。

JavaスクリプトAPIを使用してスクリプトを埋め込む方法

JavaスクリプトAPIを使用するには:

1. ScriptEngineManagerオブジェクトを作成します。

2. マネージャからScriptEngineオブジェクトを取得します。

3. スクリプト・エンジンのeval()メソッドを使用してスクリプトを評価します。

Javaのクラスを使用したJavaスクリプトAPIの例

次の例は、JavaでJavaスクリプトAPIを使用する方法を示しています。例を簡単にするため、例外は処理されていません。ただし、JavaスクリプトAPIによってスローされるチェック例外と実行時例外が存在するため、それらを適切に処理する必要があります。すべての例で、ScriptEngineManagerクラスのインスタンスとgetEngineByName()メソッドを使用して、Nashornエンジン(ScriptEngineクラスのインスタンス)を要求します。指定されたエンジンが存在しない場合は、nullを返します。Nashornエンジンの使用方法の詳細は、『Nashornユーザーズ・ガイド』を参照してください。

注意:

各ScriptEngineオブジェクトには、固有の変数スコープがあります。「複数スコープの使用」を参照してください。

文の評価

この例では、スクリプト・エンジン・インスタンスのeval()メソッドを呼び出し、StringオブジェクトのJavaScriptコードを実行します。

import javax.script.*;

public class EvalScript {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// evaluate JavaScript code
engine.eval("print('Hello, World')");
}
}

スクリプト・ファイルの評価

この例では、eval()メソッドがscript.jsという名前のファイルからJavaScriptコードを読み取るFileReaderオブジェクトを取ります。様々な入力ストリーム・オブジェクトをリーダーとしてラップすることにより、ファイル、URLおよびその他のリソースからスクリプトを実行できます。

import javax.script.*;

public class EvalFile {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// evaluate JavaScript code
engine.eval(new java.io.FileReader("script.js"));
}
}

Javaオブジェクトをグローバル変数として公開する

この例では、Fileオブジェクトを作成し、put()メソッドを使用してそれをfileという名前のグローバル変数としてエンジンに公開します。次に、この変数にアクセスしてgetAbsolutePath()メソッドを呼び出すJavaScriptコードを指定して、eval()メソッドを呼び出します。

注意:

変数として公開されたJavaオブジェクトのフィールドにアクセスしてメソッドを呼び出す構文は、スクリプト言語によって異なります。この例では、Javaによく似たJavaScript構文を使用します。

import javax.script.*;
import java.io.*;

public class ScriptVars {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// create File object
File f = new File("test.txt");

// expose File object as a global variable to the engine
engine.put("file", f);

// evaluate JavaScript code and access the variable
engine.eval("print(file.getAbsolutePath())");
}
}

スクリプト関数の呼出し

この例では、1つのパラメータを持つ関数を定義するJavaScriptコードを指定してeval()メソッドを呼び出します。次に、Invocableオブジェクトを作成し、そのinvokeFunction()メソッドを使用して関数を呼び出します。

注意:

すべてのスクリプト・エンジンがInvocableインタフェースを実装しているわけではありません。この例ではNashornエンジンを使用しており、このエンジンによって前に評価されたスクリプト内の関数を呼び出すことができます。

import javax.script.*;

public class InvokeScriptFunction {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// evaluate JavaScript code that defines a function with one parameter
engine.eval("function hello(name) { print('Hello, ' + name) }");

// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;

// invoke the function named "hello" with "Scripting!" as the argument
inv.invokeFunction("hello", "Scripting!");
}
}

スクリプト・オブジェクトのメソッドの呼出し

この例では、1つのメソッドを持つオブジェクトを定義するJavaScriptコードを指定してeval()メソッドを呼び出します。次に、スクリプト・エンジンのget()メソッドを使用して、このオブジェクトをスクリプトからJavaアプリケーションに公開します。次に、Invocableオブジェクトを作成し、そのinvokeMethod()メソッドを使用してスクリプト・オブジェクトに定義されたメソッドを呼び出します。

注意:

すべてのスクリプト・エンジンがInvocableインタフェースを実装しているわけではありません。この例ではNashornエンジンを使用しており、このエンジンによって前に評価されたスクリプト内のメソッドを呼び出すことができます。

import javax.script.*;

public class InvokeScriptMethod {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// evaluate JavaScript code that defines an object with one method
engine.eval("var obj = new Object()");
engine.eval("obj.hello = function(name) { print('Hello, ' + name) }");

// expose object defined in the script to the Java application
Object obj = engine.get("obj");

// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;

// invoke the method named "hello" on the object defined in the script
// with "Script Method!" as the argument
inv.invokeMethod(obj, "hello", "Script Method!");
}
}

スクリプト関数を使用したJavaインタフェースの実装

この例では、関数を定義するJavaScriptコードを指定してeval()メソッドを呼び出します。次に、Invocableオブジェクトを作成し、そのgetInterface()メソッドを使用してRunnableインタフェース・オブジェクトを作成します。このインタフェースのメソッドは、一致する名前を持つスクリプト関数によって実装されます(この場合は、run()関数を使ってインタフェース・オブジェクトのrun()メソッドが実装されます)。最後に、スクリプト関数を実行する新しいスレッドを開始します。

import javax.script.*;

public class ImplementRunnable {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// evaluate JavaScript code that defines a function with one parameter
engine.eval("function run() { print('run() function called') }");

// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;

// get Runnable interface object
Runnable r = inv.getInterface(Runnable.class);

// start a new thread that runs the script
Thread th = new Thread(r);
th.start();
th.join();
}
}

スクリプト・オブジェクトのメソッドを使用したJavaインタフェースの実装

この例では、1つのメソッドを持つオブジェクトを定義するJavaScriptコードを指定してeval()メソッドを呼び出します。次に、スクリプト・エンジンのget()メソッドを使用して、このオブジェクトをスクリプトからJavaアプリケーションに公開します。次に、Invocableオブジェクトを作成し、そのgetInterface()メソッドを使用してRunnableインタフェース・オブジェクトを作成します。このインタフェースのメソッドは、一致する名前を持つスクリプト・オブジェクトのメソッドによって実装されます(この場合は、objオブジェクトのrunメソッドを使ってインタフェース・オブジェクトのrun()メソッドが実装されます)。最後に、スクリプト・オブジェクトのメソッドを実行する新しいスレッドを開始します。

import javax.script.*;

public class ImplementRunnableObject {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// evaluate JavaScript code that defines a function with one parameter
engine.eval("var obj = new Object()")
engine.eval("obj.run = function() { print('obj.run() method called') }");

// expose object defined in the script to the Java application
Object obj = engine.get("obj");

// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;

// get Runnable interface object
Runnable r = inv.getInterface(obj, Runnable.class);

// start a new thread that runs the script
Thread th = new Thread(r);
th.start();
th.join();
}
}

複数スコープの使用

この例では、スクリプト・エンジンのput()メソッドを使用して変数xをStringオブジェクトhelloに設定します。次に、eval()メソッドを使用して変数をデフォルトのスコープで出力します。次に、異なるスクリプト・コンテキストを定義し、そのスコープを使用して同じ変数を異なる値(Stringオブジェクトworld)に設定します。最後に、異なる値を表示する新しいスクリプト・コンテキストで変数を出力します。

単一のスコープは、javax.script.Bindingsインタフェースのインスタンスです。このインタフェースは、java.util.Map<String, Object>インタフェースから派生します。スコープは、名前が空でもnullでもないStringオブジェクトである名前と値のペアのセットです。javax.script.ScriptContextインタフェースは、各スコープにBindingsが関連付けられている複数のスコープをサポートします。デフォルトでは、すべてのスクリプト・エンジンにデフォルトのスクリプト・コンテキストがあります。デフォルトのスクリプト・コンテキストには、staticフィールドENGINE_SCOPEで表されるスコープが少なくとも1つ存在します。スクリプト・コンテキストでサポートされる様々なスコープをgetScopes()メソッドで取得できます。

import javax.script.*;

public class MultipleScopes {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

// set global variable
engine.put("x","hello");

// evaluate JavaScript code that prints the variable (x = "hello")
engine.eval("print(x)");

// define a different script context
ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);

// set the variable to a different value in another scope
engineScope.put("x", "world");

// evaluate the same code but in a different script context (x = "world")
engine.eval("print(x)", newContext);

3 スクリプトからのJavaの使用

この項では、スクリプトからJavaのクラスおよびインタフェースにアクセスする方法について説明します。

コード例はJavaScriptで作成されていますが、JSR 223に準拠する任意のスクリプト言語を使用できます。例をスクリプト・ファイルとして使用したり、例の式を対話型シェルで1つずつ実行したりできます。オブジェクトのフィールドやメソッドにアクセスする構文は、JavaScriptでもJavaでも同じです。

トピック

- Javaクラスへのアクセス

- Javaパッケージおよびクラスのインポート

- Java配列の使用

- Javaインタフェースの実装

- 抽象Javaクラスの拡張

- 具象Javaクラスの拡張

- Javaクラスへのアクセス

- クラスへの実装のバインド

- メソッド・オーバーロード・バリアントの選択

- データ型のマッピング

- JavaへのJSONオブジェクトの引渡し

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[][]");

Java.type()関数から返された型オブジェクトは、Javaでクラス名を使用する場合と同じようにJavaScriptで使用できます。たとえば、次のように新しいオブジェクトをインスタンス化するために使用できます。

var anArrayList = new Java.type("java.util.ArrayList");

Java型オブジェクトを使用して、新しいJavaオブジェクトをインスタンス化できます。次の例は、新しいオブジェクトをインスタンス化するときに、デフォルト・コンストラクタを使用する方法と、別のコンストラクタに引数を渡す方法を示しています。

var ArrayList = Java.type("java.util.ArrayList");
var defaultSizeArrayList = new ArrayList;
var customSizeArrayList = new ArrayList(16);

Java.type()関数から返された型オブジェクトを使用して、次のようにstaticフィールドおよびメソッドにアクセスできます。

var File = Java.type("java.io.File");
File.createTempFile("nashorn", ".tmp");

static内部クラスにアクセスするには、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

staticでない内部クラスの場合は、内部クラスのインスタンスをコンストラクタの最初の引数として渡す必要があります。

JavaScriptの型オブジェクトは、Javaクラスと同じように使用できますが、getClass()メソッドから返されるjava.lang.Classオブジェクトとは区別されます。classおよびstaticプロパティを使用して一方からもう一方を取得できます。次の例は、この区別を示しています。

var ArrayList = Java.type("java.util.ArrayList");
var a = new ArrayList;

// All of the following are true:
print("Type acts as target of instanceof: " + (a instanceof ArrayList));
print("Class doesn't act as target of instanceof: " + !(a instanceof a.getClass()));
print("Type is not the same as instance's getClass(): " + (a.getClass() !== ArrayList));
print("Type's `class` property is the same as instance's getClass(): " + (a.getClass() === ArrayList.class));
print("Type is the same as the `static` property of the instance's getClass(): " + (a.getClass().static === ArrayList));

このコンパイル時のクラス式と実行時のクラス・オブジェクトを区別する点で、JavaScriptとJavaコードは構文的にも意味的にも似ています。ただし、Javaではコンパイル時のクラス式がオブジェクトとして表現されることはないため、Classオブジェクトのstaticプロパティに相当するものはありません。

Javaパッケージおよびクラスのインポート

Javaクラスに単純名でアクセスするには、importPackage()およびimportClass()関数を使用してJavaパッケージおよびクラスをインポートします。これらの関数は、互換性スクリプト(mozilla_compat.js)に組み込まれています。次の例は、importPackage()およびimportClass()関数の使用方法を示しています。

// Load compatibility script
load("nashorn:mozilla_compat.js");
// Import the java.awt package
importPackage(java.awt);
// Import the java.awt.Frame class
importClass(java.awt.Frame);
// Create a new Frame object
var frame = new java.awt.Frame("hello");
// Call the setVisible() method
frame.setVisible(true);
// Access a JavaBean property
print(frame.title);

JavaパッケージにアクセスするにはPackagesグローバル変数(たとえば、Packages.java.util.VectorやPackages.javax.swing.JFrame)を使用しますが、標準Java SEパッケージにはショートカット(Packages.javaを表すjava、Packages.javaxを表すjavax、Packages.orgを表すorg)があります。

java.langパッケージは、そのクラスがObject、Boolean、Mathおよびその他の組込みJavaScriptオブジェクトと競合するため、デフォルトではインポートされません。さらに、Javaパッケージまたはクラスをインポートすると、JavaScriptのグローバル変数スコープと競合する可能性があります。これを避けるには、次の例に示すように、JavaImporterオブジェクトを定義し、with文を使用してインポートされたJavaパッケージおよびクラスのスコープを制限します。

// Create a JavaImporter object with specified packages and classes to import
var Gui = new JavaImporter(java.awt, javax.swing);

// Pass the JavaImporter object to the "with" statement and access the classes
// from the imported packages by their simple names within the statement's body
with (Gui) {
var awtframe = new Frame("AWT Frame");
var jframe = new JFrame("Swing JFrame");
};

Java配列の使用

Java配列オブジェクトを作成するには、最初にJava配列型オブジェクトを取得し、次にそれをインスタンス化する必要があります。JavaScriptで配列要素とlengthプロパティにアクセスする構文は、次の例に示すように、Javaと同じです。

var StringArray = Java.type("java.lang.String[]");
var a = new StringArray(5);

// Set the value of the first element
a[0] = "Scripting is great!";
// Print the length of the array
print(a.length);
// Print the value of the first element
print(a[0]);

JavaScript配列を取得したら、Java.to()メソッドを使用してそれをJava配列に変換できます。このメソッドには、JavaScript配列変数を渡し、さらに返される配列の型を文字列または型オブジェクトとして渡す必要があります。配列型引数を省略して、Object[]配列を返すこともできます。変換は、ECMAScriptの変換規則に従って行われます。次の例は、様々なJava.to()メソッドの引数を使用してJavaScript配列をJava配列に変換する方法を示しています。

// Create a JavaScript array
var anArray = [1, "13", false];

// Convert the JavaScript array to a Java int[] array
var javaIntArray = Java.to(anArray, "int[]");
print(javaIntArray[0]); // prints the number 1
print(javaIntArray[1]); // prints the number 13
print(javaIntArray[2]); // prints the number 0

// Convert the JavaScript array to a Java String[] array
var javaStringArray = Java.to(anArray, Java.type("java.lang.String[]"));
print(javaStringArray[0]); // prints the string "1"
print(javaStringArray[1]); // prints the string "13"
print(javaStringArray[2]); // prints the string "false"

// Convert the JavaScript array to a Java Object[] array
var javaObjectArray = Java.to(anArray);
print(javaObjectArray[0]); // prints the number 1
print(javaObjectArray[1]); // prints the string "13"
print(javaObjectArray[2]); // prints the boolean value "false"

Java配列を取得したら、Java.from()メソッドを使用してそれをJavaScript配列に変換できます。次の例は、現在のディレクトリ内にあるファイルのリストを含むJava配列を、同じ内容を持つJavaScript配列に変換する方法を示しています。

// Get the Java File type object
var File = Java.type("java.io.File");
// Create a Java array of File objects
var listCurDir = new File(".").listFiles();
// Convert the Java array to a JavaScript array
var jsList = Java.from(listCurDir);
// Print the JavaScript array
print(jsList);

注意:

ほとんどの場合、Java配列をJavaScript配列に変換しなくても、スクリプトでJava配列を使用できます。

Javaインタフェースの実装

JavaScriptでJavaインタフェースを実装する構文は、Javaで匿名クラスを宣言する場合とほぼ同じです。インタフェースをインスタンス化し、同じ式でそのメソッドを(JavaScript関数として)実装します。次の例は、Runnableインタフェースの実装方法を示しています。

// Create an object that implements the Runnable interface by implementing
// the run() method as a JavaScript function
var r  = new java.lang.Runnable() {
run: function() {
print("running...\n");
}
};

// The r variable can be passed to Java methods that expect an object implementing
// the java.lang.Runnable interface
var th = new java.lang.Thread(r);
th.start();
th.join();

あるメソッドがメソッドを1つだけ持つインタフェースを実装するオブジェクトを受け取る場合は、このメソッドにオブジェクトのかわりにスクリプト関数を渡すことができます。たとえば、前の例のThread()コンストラクタは、メソッドが1つだけ定義されたRunnableインタフェースを実装するオブジェクトを受け取ります。自動変換を利用して、オブジェクトのかわりにスクリプト関数をこのThread()コンストラクタに渡すことができます。次の例は、Runnableインタフェースを実装せずにThreadオブジェクトを作成する方法を示しています。

// Define a JavaScript function
function func() {
print("I am func!");
};

// Pass the JavaScript function instead of an object that implements
// the java.lang.Runnable interface
var th = new java.lang.Thread(func);
th.start();
th.join();

サブクラスに複数のインタフェースを実装するには、関連する型オブジェクトをJava.extend()関数に渡します。「具象Javaクラスの拡張」を参照してください。

抽象Javaクラスの拡張

抽象Javaクラスの匿名サブクラスをインスタンス化するには、そのコンストラクタに、抽象メソッドを実装する関数を値とするプロパティを持つJavaScriptオブジェクトを渡します。メソッドがオーバーロードされると、JavaScript関数はメソッドのすべてのバリアントに実装を提供します。次の例は、TimerTask抽象クラスのサブクラスをインスタンス化する方法を示しています。

var TimerTask =  Java.type("java.util.TimerTask");
var task = new TimerTask({ run: function() { print("Hello World!") } });

コンストラクタを呼び出して引数を渡すかわりに、new式の後に引数を直接指定できます。次の例は、(Javaの匿名内部クラスの定義と同じように)この構文を使って前の例の2行目を簡単にする方法を示しています。

var task = new TimerTask {
run: function() {
print("Hello World!")
}
};

抽象クラスが関数インタフェースである(抽象メソッドが1つのみある)場合は、コンストラクタにJavaScriptオブジェクトを渡すかわりに、そのメソッドを実装する関数を渡すことができます。次の例は、関数インタフェースを使用して構文を簡素化する方法を示しています。

var task = new TimerTask(function() { print("Hello World!") });

どちらの構文を選んでも、引数を指定してコンストラクタを呼び出す必要がある場合は、実装オブジェクトまたは関数の前に引数を指定できます。

関数インタフェースを引数として取るJavaメソッドを呼び出す場合は、そのメソッドにJavaScript関数を渡すことができます。Nashornは、期待されるクラスのサブクラスをインスタンス化し、その関数を使用して唯一の抽象メソッドを実装します。次の例は、TimerTaskオブジェクトを引数として受け取るTimer.schedule()メソッドを呼び出す方法を示しています。

var Timer = Java.type("java.util.Timer");
Timer.schedule(function() { print("Hello World!") });

注意:

前の構文は、想定されている関数インタフェースがインタフェースであるか、想定されているクラスのサブクラスをNashornがインスタンス化するために使用するデフォルト・コンストラクタが関数インタフェースにあることを前提としています。デフォルト以外のコンストラクタは使用できません。

具象Javaクラスの拡張

あいまいさを回避するため、抽象クラスを拡張する構文では具象クラスを使用できません。具象クラスはインスタンス化できるため、このような構文は、クラスの新しいインスタンスを作成し、そのインスタンスにコンストラクタが受け取るオブジェクトを渡そうとしていると解釈される可能性があります(期待されるオブジェクト型がインタフェースである場合)。この具体例として、次の例を考えてみます。

var t = new java.lang.Thread({ run: function() { print("Thread running!") } });

このコードは、run()メソッドの指定された実装を使用してThreadクラスを拡張していると解釈することも、Runnableインタフェースを実装するオブジェクトをコンストラクタに渡すことでThreadクラスをインスタンス化していると解釈することもできます。「Javaインタフェースの実装」を参照してください。

具象Javaクラスを拡張するには、その型オブジェクトを、サブクラスの型オブジェクトを返すJava.extend()関数に渡します。次に、指定されたメソッド実装を使ってサブクラスのインスタンスを作成するために、サブクラスの型オブジェクトをJavaScriptからJavaへのアダプタとして使用します。次の例は、run()メソッドの指定された実装を使ってThreadクラスを拡張する方法を示しています。

var Thread = Java.type("java.lang.Thread");
var threadExtender = Java.extend(Thread);
var t = new threadExtender() {
run: function() { print("Thread running!") }};

Java.extend()関数は、複数の型オブジェクトのリストを取ることができます。Javaクラスの型オブジェクトは1つしか指定できませんが、Javaインタフェースの型オブジェクトは必要な数だけ指定できます。返された型オブジェクトは、指定されたクラス(クラスを指定しなかった場合はjava.lang.Object)を拡張し、すべてのインタフェースを実装します。クラスの型オブジェクトがリストの先頭である必要はありません。

スーパークラスのメソッドへのアクセス

スーパークラスのメソッドにアクセスするには、Java.super()関数を使用します。例3-1は、java.lang.Exceptionクラスを拡張して、スーパークラスのメソッドにアクセスする方法を示しています。

例3-1のコードを実行すると、標準出力に次が出力されます。

jdk.nashorn.javaadapters.java.lang.Exception: MY EXCEPTION MESSAGE

例3-1 スーパークラスのメソッドへのアクセス(super.js)

var Exception = Java.type("java.lang.Exception");
var ExceptionAdapter = Java.extend(Exception);

var exception = new ExceptionAdapter("My Exception Message") {
getMessage: function() {
var _super_ = Java.super(exception);
return _super_.getMessage().toUpperCase();
}
}

try {
throw exception;
} catch (ex) {
print(exception);
}

クラスへの実装のバインド

前の項では、Javaクラスを拡張し、実装を指定するコンストラクタの追加のJavaScriptオブジェクト・パラメータを使用してインタフェースを実装する方法について説明しました。したがって、実装はクラス全体ではなく、newで作成された実際のインスタンスにバインドされます。これにはいくつかの利点があります。たとえば、Nashornは実装されるあらゆる型の組合せに対応する単一の汎用アダプタを作成できるため、実行時のメモリー・サイズの点で有利です。しかし、次の例は、異なるインスタンスが、異なるJavaScript実装オブジェクトを持つにもかかわらず、同じJavaクラスを持つことを示しています。

var Runnable = java.lang.Runnable;
var r1 = new Runnable(function() { print("I'm runnable 1!") });
var r2 = new Runnable(function() { print("I'm runnable 2!") });
r1.run();
r2.run();
print("We share the same class: " + (r1.class === r2.class));

前の例は次を出力します。

I'm runnable 1!
I'm runnable 2!
We share the same class: true

インスタンス化するクラスを外部APIに渡す場合は(たとえば、JavaFXフレームワークを使用するときは、ApplicationクラスをJavaFX APIに渡してインスタンス化します)、クラスのインスタンスではなくクラスにバインドされた実装を使ってJavaクラスを拡張するか、またはインタフェースを実装する必要があります。実装をクラスにバインドするには、実装を含むJavaScriptオブジェクトをJava.extend()関数の最後の引数として渡します。これにより、コンストラクタで追加の実装オブジェクト・パラメータが不要になるため、元のクラスと同じコンストラクタを持つクラスが作成されます。次の例は、実装をクラスにバインドする方法と、その場合は呼出しが異なれば実装クラスも異なることを示しています。

var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") });
var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") });
var r1 = new RunnableImpl1();var r2 = new RunnableImpl2();
r1.run();
r2.run();
print("We share the same class: " + (r1.class === r2.class));

前の例は次を出力します。

I'm runnable 1!
I'm runnable 2!
We share the same class: false

実装オブジェクトをコンストラクタの呼出しからJava.extend()関数の呼出しに移動すると、コンストラクタの呼出しで追加の引数が不要になります。クラス固有の実装オブジェクトを使ってJava.extend()関数を呼び出すたびに、新しいJavaアダプタ・クラスが生成されます。クラスにバインドされた実装を持つアダプタ・クラスも、特定のインスタンスの動作をさらにオーバーライドするために、追加のコンストラクタ引数を取ることができます。このように、Java.extend()関数に渡されたクラスベースのJavaScript実装オブジェクトで実装の一部を提供する方法と、コンストラクタに渡されたオブジェクトでインスタンス用の実装を提供する方法の2つを組み合わせることができます。コンストラクタに渡されたオブジェクトで定義される関数は、クラスにバインドされたオブジェクトで定義される関数をオーバーライドします。次の例は、クラスにバインドされたオブジェクトで定義される関数をコンストラクタに渡された関数でオーバーライドする方法を示しています。

var RunnableImpl = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") });
var r1 = new RunnableImpl();
var r2 = new RunnableImpl(function() { print("I'm runnable 2!") });
r1.run();
r2.run();
print("We share the same class: " + (r1.class === r2.class));

前の例は次を出力します。

I'm runnable 1!
I'm runnable 2!
We share the same class: true

メソッド・オーバーロード・バリアントの選択

Javaメソッドは、引数の型でオーバーロードできます。Javaコンパイラ(javac)は、コンパイル時に正しいメソッド・バリアントを選択します。Nashornから呼び出されたJavaメソッドのオーバーロードの解決は、メソッドが呼び出された時点で行われます。正しいメソッド・バリアントが引数の型に基づいて自動的に選択されます。ただし、実際の引数の型に本当のあいまいさがある場合は、特定のオーバーロード・バリアントを明示的に指定できます。これにより、Nashornエンジンは呼出し時にオーバーロードの解決を行う必要がなくなるため、パフォーマンスが向上する可能性があります。

オーバーロード・バリアントは、特別なプロパティとして公開されます。これらは、メソッドの名前とそれに続くカッコで囲まれた引数の型からなる文字列の形式で参照できます。次の例は、引数としてObjectクラスを受け取るSystem.out.println()メソッドのバリアントを呼び出し、それに"hello"を渡す方法を示しています。

var out = java.lang.System.out;
out["println(Object)"]("hello");

前の例では、非修飾クラス名(Object)で正しいシグネチャが一意に識別されるため、これで十分です。シグネチャに完全修飾のクラス名を使用する必要があるのは、同一の非修飾名を持つ2つのオーバーロード・バリアントが異なるパラメータ型を使用する場合のみです(これは、異なるパッケージに同じ名前を持つパラメータ型が含まれていた場合に起こる可能性があります)。

データ型のマッピング

JavaとJavaScript間のほとんどの変換は、期待どおりに機能します。これまでの項では、JavaとJavaScript間のあまり目立たないデータ型マッピングについていくつか説明しました。配列はJava配列タイプ(java.util.List、java.util.Collection、java.util.Queue、java.util.Dequeなど)に自動的に変換されます。JavaScriptの関数はJavaのメソッドにパラメータとして渡されるときにSAMタイプに自動的に変換されます。すべてのJavaScriptオブジェクトにjava.util.Mapインタフェースが実装されて、APIがマップを直接受け取れるようになっています。Java APIに数値が渡されると、その数値は期待されるターゲット数値型(boxedまたはプリミティブ)に変換されます。ただし、ターゲット型が限定的でない場合(Numberなど)は、Number型になることしか期待できず、その型がボクシングされたDouble、Integer、Longおよびその他であるかどうかを特に検査する必要があります。数値は内部の最適化によって任意のboxed型になります。また、JavaScriptの仕様で定義されているToNumber変換アルゴリズムがJavaScriptの値に適用されるため、Boxed型またはプリミティブ型の数値を想定しているJava APIにJavaScriptの値を渡すことができます。StringまたはBooleanのオブジェクトをJavaのメソッドが想定している場合は、JavaScriptの仕様で定義されているToStringおよびToBooleanの変換で許容されているすべての変換を使用して値が変換されます。Nashornは、外部への公開時に内部のJavaScript文字列がjava.lang.Stringに変換されることを保証します。

JavaへのJSONオブジェクトの引渡し

Java.asJSONCompatible(obj)関数は、スクリプト・オブジェクトを受け入れて、ほとんどのJava JSONライブラリの想定と互換性があるオブジェクトを返します。すべての配列は(Mapオブジェクトではなく)Listオブジェクトとして公開され、他のすべてのオブジェクトはMapオブジェクトとして公開されます。

「データ型のマッピング」では、Java APIへの公開時に、すべてのJavaScriptオブジェクトにjava.util.Mapインタフェースが実装されると記述されています。これはJavaScript配列にも当てはまります。ただし、JavaコードがJSON解析オブジェクトを必要とする場合、この動作は通常望ましくないか、必要ありません。かわりに、JSON解析オブジェクトを操作するJavaライブラリは、通常配列がJava.util.Listインタフェースを公開することを想定しています。配列がマップではなくリストとして公開されるような方法でJavaScriptオブジェクトを公開する必要がある場合は、Java.asJSONCompatible(obj)関数を使用します。ここで、objはJSONオブジェクト・ツリーのルートです。

例3-2 Java.asJSONCompatible()関数の例

次の例は、JSON.parse()関数とJava.asJSONCompatible()関数を同じJSONオブジェクトで呼び出しています。JSON.parse()関数は配列[2,4,5]をマップとして解析するのに対し、Java.asJSONCompatible()関数は同じ配列をリストとして解析します。

import javax.script.*;
import java.util.*;

public class JSONTest {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");

Object obj1 = e.eval(
"JSON.parse('{ \"x\": 343, \"y\": \"hello\", \"z\": [2,4,5] }');");
Map<String, Object> map1 = (Map<String, Object>)obj1;
System.out.println(map1.get("x"));
System.out.println(map1.get("y"));
System.out.println(map1.get("z"));
Map<Object, Object> array1 = (Map<Object, Object>)map1.get("z");
array1.forEach((a, b) -> System.out.println("z[" + a + "] = " + b));

System.out.println();

Object obj2 = e.eval(
"Java.asJSONCompatible({ \"x\": 343, \"y\": \"hello\", \"z\": [2,4,5] })");
Map<String, Object> map2 = (Map<String, Object>)obj2;
System.out.println(map2.get("x"));
System.out.println(map2.get("y"));
System.out.println(map2.get("z"));
List<Object> array2 = (List<Object>)map2.get("z");
array2.forEach(a -> System.out.println(a));
}
}

この例の出力は次のとおりです。

343
hello
[object Array]
z[0] = 2
z[1] = 4
z[2] = 5

343
hello
[2, 4, 5]
2
4
5

Oracleの法律上の注意点

コピーライト

Copyright © 1994-2017, Oracle and/or its affiliates. All rights reserved.

ライセンス制約の保証と結果的に生じる損害の免責

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

保証免責

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクルまでご連絡ください。

制限付権利について

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

危険な用途への使用について

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、Oracle Corporationおよびその関連会社は一切の責任を負いかねます。

商標と登録商標について

OracleおよびJavaはオラクルおよびその関連会社の登録商標です。その他の社名、商品名等は各社の商標または登録商標である場合があります。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

第三者のコンテンツ、製品、サービスに対する免責事項

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

Pre-General Availability (一般提供前)版のドキュメントについて

このドキュメントがPre-General Availability (一般提供前)版の場合:

このドキュメントはPre-General Availability (一般提供前)版であり、デモおよび暫定使用のみを目的としたものです。このソフトウェアを使用するハードウェアに限定するものではありません。Oracle Corporationおよびその関連会社は、このドキュメントに関して一切の責任を負わず、いかなる保証もいたしません。また、このドキュメントを使用したことによって損失、費用、あるいは損害が発生しても、一切の責任を負いかねます。

非公開のPre-General Availability (一般提供前)版のドキュメントについて

このドキュメントが非公開のPre-General Availability (一般提供前)版の場合:

このドキュメントに含まれる情報は、情報の共有のみを目的としたものであって、顧客諮問委員会(Customer Advisory Board)のメンバーとしてのお客様の立場でのみ、またはお客様のPre-General Availability Trial Agreement (Pre-GAトライアル契約)のみに従って検討されるべきものとします。マテリアル、コード、または機能の提供をお約束するものではなく、購入決定を行う際の判断材料とすべきではありません。このドキュメントに記載されている特徴または機能の開発、リリースおよび時期については、オラクルの裁量により決定されます。

このドキュメントには、ソフトウェアまたは印刷物などの形式を問わず、オラクルが独占的な権利を有する財産的情報が含まれています。この機密資料へのアクセスと使用は、お客様とオラクルとの間で締結され、お客様が遵守に同意した、Oracle Master Agreement、Oracle License and Oracle Services Agreement (オラクル・ライセンスおよびサービスに関する契約書)、Oracle Partner Network Agreement、Oracle Distribution Agreementまたはその他のライセンス契約の条件に従うものとします。このドキュメントとその内容の開示、コピー、複製および配布には、オラクルによる事前の承諾を必要とします。このドキュメントはライセンス契約の一部となるものではなく、オラクルおよびその子会社や関連会社との契約を構成するものではありません。

ドキュメントのアクセシビリティ

オラクルのアクセシビリティについての詳細情報は、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)を参照してください。

Oracle Supportへのアクセス

サポートをご契約のお客様には、My Oracle Supportを通して電子支援サービスを提供しています。詳細情報は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info)か、聴覚に障害のあるお客様は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs)を参照してください。