2012-09-25

Rhino1.7R4で同梱のSwingApplication.jsを実行するとエラーが起きる

JavaScript Functions as Java Interfaces の後半に書かれている「2つ以上のメソッドを持つインターフェースが必要な箇所に、 関数を1つだけ渡す(関数の最後の引数に呼ばれたメソッド名が設定されるので、実行時に分岐できる)」 という方法を使うとエラーが発生する。
Rhino1.7R3(Java1.7付属のRhinoも含む)までは発生しない。 Rhino1.7R4でInterfaceAdapter.javaの実装が以下のように変更されたことが原因。
  • 変更前: スクリプトで実装しようとしているJavaインターフェースが複数のメソッドを持つ場合、 引数の数とデータ型が全て一致していればOK(例えば、WindowListenerやKeyListenerのようなGUIイベントリスナ)
  • 変更後: スクリプトで実装しようとしているJavaインターフェースが複数のメソッドを持つ場合、 許されるのはオーバーロードだけ(名前が同じで引数の数とデータ型だけが異なるメソッド)
a-1. SwingApplication.jsを実行する:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug SwingApplication.js
js: "SwingApplication.js", line 69: Cannot convert function to interface java.awt.event.WindowListener since it contains methods with different names
        at SwingApplication.js:69


b-1. 問題の実装方法を中心に記述したスクリプト(event-listener.js):
var gui = new java.awt.Frame("event-listener");
gui.addWindowListener(
    function(event, methodName) {
        if (methodName === "windowClosing") {
            event.getWindow().setVisible(false);
        }
    }
);
gui.setSize(200, 100);
gui.setVisible(true);
java.lang.Thread.currentThread().sleep(1000);
gui.dispose();
java.lang.System.out.println("ok");

b-2. Rhino1.7R4で実行する:
>java -classpath .;js.jar org.mozilla.javascript.tools.shell.Main -w -debug event-listener.js
js: "event-listener.js", line 3: Cannot convert function to interface java.awt.event.WindowListener since it contains methods with different names
        at event-listener.js:3

b-3. jrunscript(Java1.7、Rhino1.7R3 PRERELEASE相当)で実行する:
>jrunscript event-listener.js
ok

上記の動作が開発側の意図したものかどうか、New in Rhino 1.7R4に説明されていないので判断できない。
エラーの回避策としては、Rhino1.7R3を使い続けるのが簡単だが、 書き直すことが手間でないと感じるなら Implementing Java Interfaces で説明されているやり方に変更する方法も一応ある。

c-1. Javaインターフェースの全てのメソッドをスクリプトで実装する例:
var listener = new java.awt.event.WindowListener({
    windowClosing: function(event) { event.getWindow().dispose(); },
    windowClosed: function(event) { },
    windowIconified: function(event) { },
    windowOpened: function(event) { java.lang.System.out.println("opened"); },
    windowDeiconified: function(event) { },
    windowActivated: function(event) { java.lang.System.out.println("activated"); },
    windowDeactivated: function(event) { }
});

0 件のコメント:

コメントを投稿