1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
---
title: Java のスクリプティング
slug: Rhino/Scripting_Java
tags:
- Rhino
translation_of: Mozilla/Projects/Rhino/Scripting_Java
---
<p>この記事では Rhino を使用して JavaScript を超えて Java に到達する方法を説明します。Java によるスクリプティングには多くの用途があります。これは、利用可能な多くの Java ライブラリを利用して、強力なスクリプトを素早く作成することを可能にします。スクリプトを書くことで Java クラスをテストできます。 私たちは、探索的プログラミングのためのスクリプトを使用して、Java 開発を支援することもできます。探索的プログラミングとは、ライブラリや API がそれを使用するクイックプログラムを書くことによって何ができるのかを学習するプロセスです。ここからわかるように、スクリプトによってこのプロセスが簡単になります。</p>
<p>ECMA 標準では Java (またはそのような外部オブジェクトシステムとの通信) は扱いません。したがって、この章で扱うすべての機能を拡張機能と見なす必要があります。</p>
<h3 id="Accessing_Java_Packages_and_Classes" name="Accessing_Java_Packages_and_Classes">Java パッケージとクラスへのアクセス</h3>
<p>すべての Java コードはクラスの一部です。すべての Java クラスはパッケージの一部です。ただし、JavaScript では、スクリプトはパッケージ階層の外に存在します。どうしたら Java パッケージのクラスにアクセスできるでしょうか?</p>
<p>Rhino は <code style="font-size: 1em; white-space: nowrap;">Packages</code> という名前の最上位変数を定義します。<code style="font-size: 1em; white-space: nowrap;">Packages</code> 変数のプロパティはすべて <code style="font-size: 1em; white-space: nowrap;">java</code> や <code style="font-size: 1em; white-space: nowrap;">com</code> などのトップレベルの Java パッケージです。たとえば、<code style="font-size: 1em; white-space: nowrap;">java</code> パッケージの値にアクセスできます。</p>
<pre class="code">js> Packages.java
[JavaPackage java]</pre>
<p>便利なショートカットとして、Rhino は <code>Packages.java</code> と同等のトップレベルの変数 <code>java</code> を定義しています。したがって、前の例はさらに短くなる可能性があります。</p>
<pre class="code">js> java
[JavaPackage java]
</pre>
<p>パッケージの階層を下げるだけで Java クラスにアクセスできます。</p>
<pre class="code">js> java.io.File
[JavaClass java.io.File]
</pre>
<p>スクリプトが多数の異なる Java クラスにアクセスすると、毎回そのクラスの完全なパッケージ名を使用するのが面倒になることがあります。Rhino は Java の <code>import</code> 宣言と同じ目的を果たすトップレベル関数 <code>importPackage</code> を提供します。たとえば、<code>java.io</code> パッケージ内のすべてのクラスをインポートし、<code>File</code> という名前だけを使用して <code>java.io.File</code> クラスにアクセスできます。</p>
<pre class="code">js> importPackage(java.io)
js> File
[JavaClass java.io.File]
</pre>
<p>ここで <code>importPackage(java.io)</code> は、<code>java.io</code> パッケージ内のすべてのクラス (<code>File</code> など) を最上位レベルで使用可能にします。これは <code>import java.io.*;</code> Java 宣言と実質的に同じです。</p>
<p>Java では <code>java.lang.*</code> が暗黙的にインポートされるのに対し、Rhino はそうでないことに注意することが重要です。その理由は、JavaScript には <code>java.lang</code> パッケージで定義された名前とは異なる独自のトップレベルオブジェクト <code>Boolean</code>、<code>Math</code>、<code>Number</code>、<code>Object</code>、および <code>String</code> があるからです。この競合のため、<code>java.lang</code> パッケージで <code>importPackage</code> を使用しないことをお勧めします。</p>
<p>注意すべき点の1つは、Rhino が Java パッケージまたはクラス名を指定する際にエラーを処理することです。<code>java.MyClass</code> にアクセスすると、Rhino は <code>java.MyClass</code> という名前のクラスのロードを試みます。そのロードに失敗すると、<code>java.MyClass</code> はパッケージ名であるとみなされ、エラーは報告されません。</p>
<pre class="code">js> java.MyClass
[JavaPackage java.MyClass]
</pre>
<p>このオブジェクトをクラスとして使用しようとした場合にのみ、エラーが報告されます。</p>
<h4 id="External_Packages_and_Classes" name="External_Packages_and_Classes">外部パッケージとクラス</h4>
<p>Rhino で外部パッケージやクラスを使用することもできます。<code>.jar</code> または <code>.class</code> ファイルがクラスパス上にあることを確認してから、JavaScript アプリケーションにインポートすることができます。これらのパッケージは <code>java</code> パッケージにはない可能性が高いので、パッケージ名の前に "<code>Packages.</code>" を付ける必要があります。たとえば、<code>org.mozilla.javascript</code> パッケージをインポートするには、次のように <code>importPackage()</code> を使用できます。</p>
<pre class="code">$ java org.mozilla.javascript.tools.shell.Main
js> importPackage(Packages.org.mozilla.javascript);
js> Context.currentContext;
org.mozilla.javascript.Context@bb6ab6
</pre>
<p>場合によっては、<code>importClass()</code> メソッドを使用してインポートするのではなく、例にあるようにパッケージの完全修飾名を使用します。これも可能ですが、入力が増えます。完全修飾名を使用すると、上記の例は次のようになります。</p>
<pre class="code">$ java org.mozilla.javascript.tools.shell.Main
js> jsPackage = Packages.org.mozilla.javascript;
[JavaPackage org.mozilla.javascript]
js> jsPackage.Context.currentContext;
org.mozilla.javascript.Context@bb6ab6
</pre>
<p>また、パッケージから1つのクラスだけをインポートする場合は、<code>importClass()</code> メソッドを使用してクラスをインポートできます。上記の例は、次のように表すことができます。</p>
<pre>$ java org.mozilla.javascript.tools.shell.Main
js> importClass(Packages.org.mozilla.javascript.Context);
js> Context.currentContext;
org.mozilla.javascript.Context@bb6ab6</pre>
<h3 id="Working_With_Java" name="Working_With_Java">Java をあわせて使用する</h3>
<p>Java クラスにアクセスできるようになったので、次の論理的なステップはオブジェクトを作成することです。これは、Java の場合と同様に <code>new</code> 演算子を使用して動作します。</p>
<pre class="code">js> new java.util.Date()
Thu Jan 24 16:18:17 EST 2002
</pre>
<p>新しいオブジェクトを JavaScript 変数に格納すると、そのオブジェクトに対してメソッドを呼び出すことができます。</p>
<pre class="code">js> f = new java.io.File("test.txt")
test.txt
js> f.exists()
true
js> f.getName()
test.txt
</pre>
<p>静的メソッドおよびフィールドは、クラスオブジェクト自体からアクセスできます。</p>
<pre class="code">js> java.lang.Math.PI
3.141592653589793
js> java.lang.Math.cos(0)
1
</pre>
<p>JavaScript では、Java と異なり、メソッド自体はオブジェクトであり、呼び出されるだけでなく評価されます。メソッドオブジェクトを単独で表示すると、メソッドのさまざまなオーバーロードされた形式を見ることができます。</p>
<pre class="code">js> f.listFiles
function listFiles() {/*
java.io.File[] listFiles()
java.io.File[] listFiles(java.io.FilenameFilter)
java.io.File[] listFiles(java.io.FileFilter)
*/}
</pre>
<p>この出力は、<code>File</code> クラスが3つのオーバーロードされたメソッド <code>listFiles</code> を定義していることを示しています。1つは引数を取らず、他は <code>FilenameFilter</code> 引数を持つもの、<code>FileFilter</code> 引数を持つものです。すべてのメソッドは <code>File</code> オブジェクトの配列を返します。Java メソッドのパラメータと戻り値の型を見ることができるのは、メソッドを調べている可能性があり、パラメータや戻り値の型が不明な探索プログラミングで特に役立ちます。</p>
<p>探索的プログラミングのもう1つの有用な機能は、オブジェクトに対して定義されたすべてのメソッドとフィールドを表示する機能です。JavaScript の <code>for..in</code> 構文を使用して、これらの値をすべて出力することができます:</p>
<pre class="code">js> for (i in f) { print(i) }
exists
parentFile
mkdir
toString
wait
<em>[44 others]</em>
</pre>
<p><code>File</code> クラスのメソッドだけでなく、(<code>wait</code> のような) 基本クラス <code>java.lang.Object</code> から継承されたメソッドもリストされていることに注意してください。これにより、深くネストされた継承階層のオブジェクトを扱うことが容易になります。これは、そのオブジェクトで使用可能なすべてのメソッドを見ることができるからです。</p>
<p>Rhino は、JavaBeans のプロパティにプロパティ名で直接アクセスできるようにすることで、別の便利さを提供します。JavaBean のプロパティ <code>foo</code> は、<code>getFoo</code> と <code>setFoo</code> のメソッドで定義されています。さらに、同じ名前のブール値プロパティは、<code>isFoo</code> メソッドで定義することができます。たとえば、次のコードは実際に <code>File</code> オブジェクトの <code>getName</code> メソッドと <code>isDirectory</code> メソッドを呼び出します。</p>
<pre class="code">js> f.name
test.txt
js> f.directory
false
</pre>
<h3 id="Calling_Overloaded_Methods" name="Calling_Overloaded_Methods">オーバーロードされたメソッドの呼び出し</h3>
<p>引数の型に基づいて呼び出すメソッドを選択するプロセスはオーバーロード解決と呼ばれます。Java では、オーバーロードの解決はコンパイル時に実行され、Rhino では実行時に行われます。第2章で議論したように、JavaScript の動的型指定を使用すると、この違いは避けられません。変数の型は実行時まで認識されないため、過負荷解決が発生するだけです。</p>
<p>例として、いくつかのオーバーロードされたメソッドを定義し、それらを呼び出す以下の Java クラスを考えてみましょう。</p>
<pre class="code">public class Overload {
public String f(Object o) { return "f(Object)"; }
public String f(String s) { return "f(String)"; }
public String f(int i) { return "f(int)"; }
public String g(String s, int i) { return "g(String,int)"; }
public String g(int i, String s) { return "g(int,String)"; }
public static void main(String[] args) {
Overload o = new Overload();
Object[] a = new Object[] { new Integer(3), "hi", Overload.class };
for (int i = 0; i != a.length; ++i)
System.out.println(o.f(a[i]));
}
}
</pre>
<p>プログラムをコンパイルして実行すると、出力が生成されます</p>
<pre class="code">f(Object)
f(Object)
f(Object)
</pre>
<p>しかし、同様のスクリプトを書くと</p>
<pre class="code">var o = new Packages.Overload();
var a = [ 3, "hi", Packages.Overload ];
for (var i = 0; i != a.length; ++i)
print(o.f(a[i]));
</pre>
<p>それを実行すると、出力が得られます</p>
<pre class="code">f(int)
f(String)
f(Object)
</pre>
<p>Rhino は実行時にオーバーロードされたメソッドを選択するため、引数に一致するより具体的な型を呼び出します。その間、Java はコンパイル時に引数の型だけでオーバーロードされたメソッドを選択します。</p>
<p>これは、各呼び出しでより良い一致が可能なメソッドを選択する利点がありますが、より多くの作業が行われるためパフォーマンスに影響します。実際には、この性能コストは実際のアプリケーションでは顕著ではありません。</p>
<p>過負荷の解決は実行時に発生するため、実行時に失敗する可能性があります。たとえば、<code>Overload</code> のメソッド <code>g</code> を2つの整数で呼び出すと、どちらのメソッドの形式も他よりも引数の型が近くないため、エラーが発生します。</p>
<pre class="code">js> o.g(3,4)
js:"<stdin>", line 2: The choice of Java method Overload.g
matching JavaScript argument types (number,number) is ambiguous;
candidate methods are:
class java.lang.String g(java.lang.String,int)
class java.lang.String g(int,java.lang.String)
</pre>
<p>オーバーロードセマンティクスのより正確な定義については、<a href="http://web.archive.org/web/20110623074154/http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">Java メソッドのオーバーロードと LiveConnect 3</a> を参照してください。</p>
<h3 id="Implementing_Java_Interfaces" name="Implementing_Java_Interfaces">Java インターフェイスの実装</h3>
<p>Java クラスにアクセスし、Java オブジェクトを作成し、それらのオブジェクトのフィールド、メソッド、プロパティにアクセスできるようになったので、私たちはすぐに大きな力を持っています。しかし、それだけでは不十分な例がいくつかあります。Java の多くの API は、クライアントが実装しなければならないインターフェースを提供することで機能します。その1つの例は <code>Thread</code> クラスです。そのコンストラクタは、新しいスレッドが開始されたときに呼び出される単一メソッドの <code>run</code> を含む <code>Runnable</code> を取ります。</p>
<p>このニーズに対応するため、Rhino はインターフェイスを実装する新しい Java オブジェクトを作成する機能を提供します。まず、Java インターフェイスで必要とされるメソッドと名前が一致する関数プロパティーを持つ JavaScript オブジェクトを定義する必要があります。<code>Runnable</code> を実装するには、パラメータを指定せずに実行するメソッドを1つだけ定義する必要があります。第3章から覚えていれば、{propertyName:value} 表記で JavaScript オブジェクトを定義することは可能です。この構文を関数式と組み合わせて使用すると、run メソッドで JavaScript オブジェクトを定義できます。</p>
<pre class="code">js> obj = { run: function () { print("\nrunning"); } }
[object Object]
js> obj.run()
running
</pre>
<p><code>Runnable</code> を構築することによって、<code>Runnable</code> インターフェイスを実装するオブジェクトを作成できます。</p>
<pre class="code">js> r = new java.lang.Runnable(obj);
[object JavaObject]
</pre>
<p>Java ではインプリメンテーションが利用できないため、インターフェイス上で <code>new</code> 演算子を使用することはできません。ここで Rhino は JavaScript オブジェクト <code>obj</code> から実装を取得します。<code>Runnable</code> を実装するオブジェクトができたので、<code>Thread</code> を作成して実行することができます。<code>run</code> に対して定義した関数は、新しいスレッドで呼び出されます。</p>
<pre class="code">js> t = new java.lang.Thread(r)
Thread[Thread-2,5,main]
js> t.start()
js>
running
</pre>
<p>最後の <code>js</code> プロンプトと新しいスレッドからの出力は、スレッドのスケジューリングに応じてどちらの順序でも表示されます。</p>
<p>Rhino は <code>Runnable</code> を実装する新しい Java クラスのバイトコードを生成し、<code>run</code> メソッドへのすべての呼び出しを関連する JavaScript オブジェクトに転送します。このクラスを実装するオブジェクトは Java アダプタと呼ばれます。JavaScript への転送は実行時に行われるため、呼び出されるまでインターフェイスを実装するメソッドの定義を遅らせることができます。必要なメソッドを省略することは大規模なプログラミングにおいては悪い習慣ですが、小さなスクリプトや探索的プログラミングには便利です。</p>
<h3 id="The_JavaAdapter_Constructor" name="The_JavaAdapter_Constructor">JavaAdapter コンストラクタ</h3>
<p>前のセクションでは、Java インターフェイスで <code>new</code> 演算子を使用して Java アダプタを作成しました。このアプローチには限界があります。複数のインターフェイスを実装することは不可能であり、非抽象クラスを拡張することもできません。これらの理由から、<code>JavaAdapter</code> コンストラクタがあります。</p>
<p><code>JavaAdapter</code> コンストラクタの構文は次のとおりです。</p>
<pre class="code">new JavaAdapter(javaIntfOrClass, [javaIntf, ..., javaIntf,] javascriptObject)
</pre>
<p>ここで <code>javaIntfOrClass</code> は実装するインターフェイスまたは拡張するクラスであり、<code>javaIntf</code> は実装するための追加のインターフェイスです。<code>javascriptObject</code> は、Java アダプタから呼び出されるメソッドを含む JavaScript オブジェクトです。</p>
<p>実際には、<code>JavaAdapter</code> コンストラクタを直接呼び出す必要はほとんどありません。ほとんどの場合、<code>new</code> 演算子を使用する前の構文で十分です。</p>
<h3 id="JavaScript_Functions_as_Java_Interfaces" name="JavaScript_Functions_as_Java_Interfaces">Java インターフェイスとしての JavaScript 関数</h3>
<p>多くの場合、前述の <code>Runnable</code> の例のように、またはさまざまなイベントリスナの実装を提供する場合のように、1つのメソッドだけを持つインターフェイスを実装する必要があります。 これを容易にするために、Rhino はそのようなインターフェイスが期待されるときに JavaScript 関数を渡すことができます。この関数はインターフェイスメソッドの実装として呼び出されます。</p>
<p>以下は単純化された <code>Runnable</code> の例です:</p>
<pre class="code">js> t = java.lang.Thread(function () { print("\nrunning"); });
Thread[Thread-0,5,main]
js> t.start()
js>
running
</pre>
<p>Rhino では、すべてのメソッドが同じシグネチャを持つ場合、複数のメソッドを持つ Java インターフェイスの実装として JavaScript 関数を使用することもできます。関数を呼び出すと、Rhino はメソッドの名前を追加の引数として渡します。関数は、呼び出されたメソッドに代わって関数を使用して区別できます。</p>
<pre class="code">js> var frame = new Packages.javax.swing.JFrame();
js> frame.addWindowListener(function(event, methodName) {
if (methodName == "windowClosing") {
print("Calling System.exit()..."); java.lang.System.exit(0);
}
});
js> frame.setSize(100, 100);
js> frame.visible = true;
true
js> Calling System.exit()...
</pre>
<h3 id="Creating_Java_Arrays" name="Creating_Java_Arrays">Java 配列の作成</h3>
<p>Rhino には Java 配列を作成するための特別な構文はありません。このためには <code>java.lang.reflect.Array</code> クラスを使用する必要があります。5つの Java 文字列の配列を作成するには次の呼び出しを行います。</p>
<pre class="code">js> a = java.lang.reflect.Array.newInstance(java.lang.String, 5);
[Ljava.lang.String;@7ffe01
</pre>
<p>プリミティブ型の配列を作成するには、<code>java.lang</code> パッケージの関連オブジェクトクラスで定義されている特殊な TYPE フィールドを使用する必要があります。たとえば、バイトの配列を作成するには特別なフィールド <code>java.lang.Byte.TYPE</code> を使用する必要があります。</p>
<pre class="code">js> a = java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 2);
[C@7a84e4
</pre>
<p>結果の値は、その型の Java 配列が許可されている任意の場所で使用できます。</p>
<pre class="code">js> a[0] = 104
104
js> a[1] = 105
105
js> new java.lang.String(a)
hi
</pre>
<h3 id="Java_Strings_and_JavaScript_Strings" name="Java_Strings_and_JavaScript_Strings">Java 文字列と JavaScript 文字列</h3>
<p>Java 文字列と JavaScript 文字列は同じ<strong>ではない</strong>ことに注意してください。Java 文字列は <code>java.lang.String</code> 型のインスタンスであり、そのクラスによって定義されたすべてのメソッドを持ちます。JavaScript 文字列には <code>String.prototype</code> で定義されたメソッドがあります。最も一般的な障害は <code>length</code> です。これは Java 文字列のメソッドであり、JavaScript 文字列の動的プロパティです。</p>
<pre class="code">js> javaString = new java.lang.String("Java")
Java
js> jsString = "JavaScript"
JavaScript
js> javaString.length()
4
js> jsString.length
10
</pre>
<p>Rhino は2つの型の違いを減らすための助けとなります。まず Java 文字列を Java メソッドに渡し、Rhino が変換を実行します。 前の例の <code>java.lang.String</code> コンストラクタの呼び出しで実際にこの機能が動作していました。</p>
<p>Rhino は、java.lang.String クラスがまだそれらを定義していない場合、JavaScript メソッドを Java 文字列で使用できるようにします。例えば:</p>
<pre class="code">js> javaString.match(/a.*/)
ava
</pre>
<h3 id="JavaImporter_Constructor" name="JavaImporter_Constructor">JavaImporter コンストラクタ</h3>
<p><code>JavaImporter</code> は、Java のスクリプト作成時に明示的なパッケージ名を省略できる新しいグローバルコンストラクタです。</p>
<pre>var SwingGui = JavaImporter(Packages.javax.swing,
Packages.javax.swing.event,
Packages.javax.swing.border,
java.awt.event,
java.awt.Point,
java.awt.Rectangle,
java.awt.Dimension);
...
with (SwingGui) {
var mybutton = new JButton(test);
var mypoint = new Point(10, 10);
var myframe = new JFrame();
...
}
</pre>
<p>これまでこのような機能は <a href="/ja/docs/Rhino"><code>org.mozilla.javascript.ImporterTopLevel</code></a> クラスをトップレベルのスコープとして使用した埋め込みにのみ使用できました。このクラスでは、スクリプト用の <code>importPackage()</code> および <code>importClass()</code> グローバル関数が追加されていますが、広範囲に使用すると Java クラスの名前でグローバル名前空間が汚染され、ロードされたクラスがガベージコレクションから保護されます。</p>
<p>詳細については <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=245882">Bugzilla 245882</a> を参照してください。</p>
<h3 id="Java_Exceptions" name="Java_Exceptions">Java 例外</h3>
<p>Java メソッドによってスローされた例外は、<a href="/ja/docs/JavaScript/Guide/Exception_Handling_Statements/try...catch_Statement">try ... catch 文</a>を使用して JavaScript コードで捕捉できます。Rhino は Java 例外を次のプロパティを持つエラーオブジェクトにラップします。</p>
<ul>
<li><code>javaException</code>: Java メソッドによってスローされた元の例外</li>
<li><code>rhinoException</code>: Rhino ランタイムでラップされた例外</li>
</ul>
<p><code>instanceof</code> 演算子を使用すると、例外の型を問い合せることができます。</p>
<pre>try {
java.lang.Class.forName("NonExistingClass");
} catch (e) {
if (e.javaException instanceof java.lang.ClassNotFoundException) {
print("Class not found");
}
}
</pre>
<p>Rhino は、例外の条件付きキャッチを定義する try ... catch ステートメントの拡張もサポートしています。</p>
<pre>function classForName(name) {
try {
return java.lang.Class.forName(name);
} catch (e if e.javaException instanceof java.lang.ClassNotFoundException) {
print("Class " + name + " not found");
} catch (e if e.javaException instanceof java.lang.NullPointerException) {
print("Class name is null");
}
}
classForName("NonExistingClass");
classForName(null);
</pre>
|