原文はこちら。
https://blogs.oracle.com/nashorn/entry/fully_transparent_java_objects_in
NashornのJSAdapterは、JavaオブジェクトをJavaScriptで取り扱うための強力なツールで、完全な透過性を有していますので、これを利用してJavaオブジェクトをラップし、新たなプロパティや挙動を追加することができます。
しかし、これらの例を見ると、ラッピングが完全ではないことを示しています。
ここでラッパーが完全に透過的でなければならないのは、デフォルトの場合です。ラッパーを完全に透過的にするためにここで必要なのは、あるプロパティを追加するような、すべてのメソッド呼び出しをラップされたJavaオブジェクトへ転送するデフォルトの場合です。このケースの場合、レシーバーと引数を正しく取り扱う必要があるため、プロパティの検索に比べていささか複雑です。受信機と引数を正しく処理する必要があるため場合は、プロパティの検索よりも少し複雑です。
以下の例は、透過呼び出しの転送のために利用可能なイディオムです。
最初の行で、実際の呼び出し引数を配列から抽出しています。
2行目では、イディオムが実際に呼び出しを行う
これで。ラッピングが透過的になっています。
[訳注]
JDK 9 Nashornのソースは、以下のURLにアクセスし、
https://blogs.oracle.com/nashorn/entry/fully_transparent_java_objects_in
NashornのJSAdapterは、JavaオブジェクトをJavaScriptで取り扱うための強力なツールで、完全な透過性を有していますので、これを利用してJavaオブジェクトをラップし、新たなプロパティや挙動を追加することができます。
JSAdapter constructor例:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-JSAdapterconstructor
実行すると、このスクリプトは現在時刻を表すvar Date = Java.type('java.time.LocalDate')
function wrapDate(d) {
return new JSAdapter() {
__get__: function(name) {
if (name === 'yesterday') {
return d.minusDays(1)
}
return d[name]
}
}
}
var now = wrapDate(Date.now())
Date
オブジェクトを JSAdapter
でラップし、ラップされたオブジェクトの任意のプロパティに対するアクセスがあると、速やかに __get__ function
が呼び出されます。プロパティ名がアダプタが理解しているもの(ここでは yesterday
)であれば、適切な値を返します。その他のすべてのプロパティはラップされたオブジェクトから取得されます。jjs> load('yesterday.js')
jjs> print(now.yesterday)
2015-11-04
しかし、これらの例を見ると、ラッピングが完全ではないことを示しています。
ラッパーは、転送するように指示されたラップされたオブジェクトにそれらの要求を転送するだけの、独立したオブジェクトです。jjs> print(now)
<shell>:1 TypeError: [object JSAdapter] has no such function "toString"
jjs> print(now.lengthOfMonth())
<shell>:1 TypeError: [object JSAdapter] has no such function "lengthOfMonth"
ここでラッパーが完全に透過的でなければならないのは、デフォルトの場合です。ラッパーを完全に透過的にするためにここで必要なのは、あるプロパティを追加するような、すべてのメソッド呼び出しをラップされたJavaオブジェクトへ転送するデフォルトの場合です。このケースの場合、レシーバーと引数を正しく取り扱う必要があるため、プロパティの検索に比べていささか複雑です。受信機と引数を正しく処理する必要があるため場合は、プロパティの検索よりも少し複雑です。
以下の例は、透過呼び出しの転送のために利用可能なイディオムです。
非常に思慮深いJavaScript中のこの2行で、所望の機能を果たします。各JavaScript関数には、暗黙のローカル変数とfunction wrapDate(d) {
return new JSAdapter() {
__call__: function() {
...
var args = [d].concat(Array.prototype.slice.call(arguments, 1))
return Function.call.apply(d[arguments[0]], args)
}
}
}
arguments
があり、これは現在起動中の関数に渡されるすべての引数を保持します。このarguments
配列は2箇所で利用されています。最初の行で、実際の呼び出し引数を配列から抽出しています。
arguments
の最初の配列要素は呼び出されたメソッド名であって、これは引数になくてもかまいません。しかしレシーバーにとってはこの引数は必要で、 concat
を呼び出すことにより、その第1引数を引数の前に連結しています。2行目では、イディオムが実際に呼び出しを行う
Function.call
関数を使っています。プロパティ検索を使って、呼び出されるメソッドを表すオブジェクトをラップされたオブジェクトから取り出します。ここで、arguments
変数の最初の要素が呼び出されるメソッドの名前であることを思い出してください。レシーバーオブジェクトを含む引数は、すでに引数にargs
に存在します。これで。ラッピングが透過的になっています。
transparent forwarding idiomを説明したサンプルはJDK 9のNashornのソース(samples/jsadapter-fallthrough.js)にあります。jjs> print(now)
2015-11-05
jjs> print(now.lengthOfMonth())
30
jjs> print(now.yesterday)
2015-11-04
[訳注]
JDK 9 Nashornのソースは、以下のURLにアクセスし、
http://hg.openjdk.java.net/jdk9/jdk9/nashorn左側のメニューの[browse] をクリック > ディレクトリ samples をクリック > jsadapter-fallthrough.jsをクリックすると確認できます。