カスタムオブジェクトを Java側でつくって JavaScript であれこれしたい場合で、 そのカスタムオブジェクトが配列的なクラスの場合 list[0] のようにアクセスしたときに意図した値を返す方法.
結論としては、ScriptableObject を拡張してつくったサブクラスで Object get(int index, Scriptable start) メソッドをオーバーライドすればOK.
テストのため、このクラスはどの配列の要素にアクセスしても hello という文字列しか返さないようにしてみます. (例によりコードは groovy です.)
class MyArrayList extends ScriptableObject {
@Override
String getClassName() { return 'MyArrayList' }
@Override
Object get(int index, Scriptable start){
return 'hello'
}
}
myArrayList[0]
などを JavaScript 側で実行したときに get メソッドが呼ばれます.
このクラスは常に hello 文字列を返すだけの実装になっています.
このJavaScript を実行してみると hello 文字列がかえるはずです.
def script0 = 'new MyArrayList()[999]'
helloMyArrayList.groovy
@Grab(group='org.mozilla', module='rhino', version='1.7.11')
import org.mozilla.javascript.Context
import org.mozilla.javascript.ScriptableObject
import org.mozilla.javascript.Scriptable
class MyArrayList extends ScriptableObject {
@Override
String getClassName() { return 'MyArrayList' }
@Override
Object get(int index, Scriptable start){
return 'hello'
}
}
def script0 = 'new MyArrayList()[999]'
def cx = Context.enter()
def scope = cx.initStandardObjects()
ScriptableObject.defineClass(scope, MyArrayList.class)
def result0 = cx.evaluateString(scope, script0, "<cmd>", 1, null)
println result0
実行してみると
$ groovy helloMyArrayList.groovy
hello
MyArrayList クラスを修正して普通の配列として振る舞うようにします。 具体的には:
修正版 MyArrayList
class MyArrayList extends ScriptableObject {
private def list = new java.util.ArrayList()
@Override
String getClassName() { return 'MyArrayList' }
@Override
Object get(int index, Scriptable start){
if(0<=index && index<list.size()){
return list.get(index)
}
return super.get(index, start)
}
void jsConstructor() {}
int jsGet_length(){
return list.size()
}
void jsFunction_push(Object value){
list.add(value)
}
}
get メソッドでは、index を範囲チェックした上で、要素数以内ならば、該当要素を返し、そうでなければ、親クラスに処理を任せています.
def script0 = '''\
var list = new MyArrayList();
list.push('pika');
list[0];'''
def cx = Context.enter()
def scope = cx.initStandardObjects()
ScriptableObject.defineClass(scope, MyArrayList.class)
def result0 = cx.evaluateString(scope, script0, "<cmd>", 1, null)
println result0
実行すると意図通り pika が返ります.
これで JavaScript の配列のように振る舞うカスタムオブジェクトをJava側で用意することができました.