AWT の拡張の目次へ戻る

プログラムの 1.1 AWT APIへの変換方法

AWT のアーキテクチャは 1.1 になって大きく改善されました。この改善では、互換性に影響するような変更がいくつか行われました。特に、1.1 の機能を使用するプログラムは、それ以前のバージョンの実行システムでは 動作しません。しかし、1.1 に基づく実行システムでは、1.0、1.0.1、1.0.2 でコンパイルしたプログラムは、それ以前のバージョンでバグがない限り 動作します。(これら非互換の可能性があるバグの修正リストは、ドキュメント 1.1 AWT API の非互換変更の中にあります。) 1.0.X の機能だけを使用して 1.1 でコンパイルしたプログラムについては、1.0.X 実行システムで動作するようですが、この互換性は厳密にはテストされておらず保証はされません。

例えば、1.0 のアプレットはすべて、 1.1 でコンパイルが成功するはずです。そして古い (1.0) クラスファイルを使用するか、再コンパイルして新しい (1.1) クラスファイルを使用するかに関わらず、1.0 のアプレットは 1.1 ベースのブラウザで動作します。また、アプレットソースを変更しない限り、新しい (1.1) クラスファイルは古い (1.0ベースの) ブラウザで動作するはずです。しかし、1.1 の機能を使用するためにアプレットを変更した場合、アプレットは 1.1 API への更新をしていない Netscape 3.0 のようなブラウザでは動作しないでしょう。

さて、なぜプログラムを 1.1 AWT API に更新する必要があるのでしょうか? 次の 3 つの理由があります:

このドキュメントの後半では、 AWT の変更内容 および プログラムの変換方法 について説明します。そして、プログラムを 1.1 に変換する を示します。

AWT の変更内容

AWTの変更は次の 3 つの方法で行われました:

イベント処理の変更の詳細は このドキュメントの後半にあります。AWT の変更内容の詳細については、 JDK1.1 - AWT の拡張を参照してください。

プログラムの変換方法

プログラムを 1.1 AWT に変換するには、次の 2 つの大きなフェーズがあります:

一般的な操作説明

  1. 最初に元のプログラム、Java ソースコードと Java バイトコードの両方のコピーを保存します。そのプログラムを実行する Java 実行システムのすべてが 1.1 に変換されるまで、そのコピーは必要です。例えば、 Netscape や Microsoft などのライセンシーが自社製品に 1.1 を組み込んで更新するためには、最終の JDK 1.1 リリースから数ヶ月を要する可能性があります。こうした理由から、アプレットの予定ユーザが新バージョンにアップグレードするまで、1.1 ベースのアプレットを Web へリリースするのを遅らせたい (または 1.0 と 1.1 バージョンを両方提供したい) ということがあるかもしれません。 次に UNIX システム上でプログラムのコピーを保存する例を示します:
    % cp MyClass.java MyClass.java.orig
    % cp MyClass.class MyClass.class.orig
    
  2. ソースコードを変換します-- 可能ならば自動的に-- こうして名前が変更されたメソッドの新しい名前を使用するようにします。

    ほとんどの変換を自動的に実行する sed スクリプトが提供されます。(スクリプトがなにを変換するかの詳細については、単純な名前変更を参照してください。) このスクリプトは UNIX sed コマンド用に設計されています。PC 上で開発をしている場合、UNIX ツールの PC バージョンを提供する MKS Toolkit などの製品を使用して、スクリプトを実行可能です。次に UNIX システム上のシェルプロンプトで sed スクリプトの使用方法を示します:

    % updateAWT MyClass.java > tmp.java
    

    変更をチェックし、明らかな異常が起こらないことを確認します。例えば、UNIX システム上で次のコマンドを実行できます:

    % diff MyClass.java tmp.java
    
    変更が OK であることを確認後、次のようにします:
    % mv tmp.java MyClass.java
    

  3. 1.1 でプログラムのコンパイルを試みます。1.1 コンパイラを使用していることを確認します。次に UNIX システム上でのコンパイル例を示します:
    % javac MyClass.java
    

    コンパイルエラーまたは警告が表示される可能性があります。最も多いものは、プログラムが推奨されないメソッドを使用した、またはメソッドが見つからなかった、という警告です。

    プログラムが、ある理由から使用を推奨しない 1.1 以前のもののような、推奨されないメソッドを呼び出した場合、コンパイラは警告を表示します。 例えば:

    % javac MyClass.java
    Note: MyClass.java uses a deprecated API.  Recompile with "-deprecation"
    for details.
    1 warning
    %
    

    注: 1.1.1 Java コンパイラは、プログラムが推奨されないメソッドをオーバーライドするとき警告を出しますが、1.1 コンパイラは出しません。1.1 コンパイラが警告を出すのは、プログラムが推奨されないメソッドを呼び出しするときだけです。例えば、プログラムが action メソッドをオーバーライドする場合、action の実装のほとんどがスーパークラスの実装を呼び出ししないため、1.1 コンパイラは通常警告を出しません。

    各ソースファイルが推奨されないメソッドを使用しているかどうかの情報を得るためには、Java コンパイラの -deprecation オプションを使用します。例えば次のようになります:

    % javac -deprecation MyClass.java
    MyClass.java:18: Note: The method boolean handleEvent(java.awt.Event) in
    class java.awt.Component has been deprecated, and class MyClass overrides
    it.
        public boolean handleEvent(Event event) {
                       ^
    MyClass.java:26: Note: The method boolean handleEvent(java.awt.Event) in
    class java.awt.Component has been deprecated.
            return super.handleEvent(event);
                                    ^
    Note: MyClass.java uses a deprecated API.  Please consult the
    documentation for a better alternative.
    2 warnings
    

    推奨されないメソッドの代替については、1.1 AWT の推奨されないメソッドで調べることができます。(それは逆にイベント関連メソッドについては、このドキュメントを参照するように指示しています。)

    メソッドを見つけることができなかったというコンパイラエラーを受け取った場合、sed スクリプトがメソッドの名前を不正に変更した可能性があります。この問題を修正するためのヘルプについては、単純な名前変更 を参照してください。

  4. プログラムのコンパイルで警告がなくなるまで、手作業で残りの変更を行います。手作業で行う変更には、1.1 イベントモデルへの変換があります。このドキュメントでは 1.1 イベントモデルの概要を示し、イベント処理コードの変換方法を説明します。

  5. プログラムを実行してテストします。

イベント処理コードの変換方法

まず最初に、1.1 以前でどのようにイベント処理がなされていたかを検証してみましょう。1.1 以前では、Component handleEvent メソッドが(action など、それが呼び出すメソッドと共に)イベント処理の中心でした。 Component オブジェクトだけがイベントを処理でき、イベントを処理するコンポーネントは、イベントが発生するコンポーネントか、またはコンポーネント包含階層の上位コンポーネントでなければなりませんでした。

1.1 では、もはやイベント処理はコンポーネント包含階層のオブジェクトに限定されず、handleEvent メソッドはイベント処理の中心ではなくなりました。代わりに、どのタイプのオブジェクトでもイベントリスナとして登録できます。イベントリスナは、関心のある登録したイベントの型についてだけ通知を受け取ります。イベントを処理するためだけに Component サブクラスを生成する必要はもうなくなりました。

バージョン 1.1 へアップグレードするとき、イベント処理コードを変換する最も簡単な方法は、同じクラスにそれを残し、そのクラスをそのイベントの型のリスナにすることです。

別の可能性としては、イベント処理コードを、 1 つまたはそれ以上の非コンポーネントリスナに集中化することです。このアプローチを取れば、プログラムの GUI を実装の詳細から分離できます。このためには、実在するコードを修正し、リスナがコンポーネントから必要とするすべての状態の情報を得られるようにする必要があります。このアプローチは、プログラムのアーキテクチャを明瞭に保っておきたい場合に価値があります。

第 3 の可能性としては、processXxx メソッドの 1 つをオーバー ライドすることですが、Component を既に拡張している場合でない限り推奨しません。これらのイベント処理アプローチについては、設計ドキュメント delegation イベントモデル で取り上げます。

同じプログラム内で 1.0 イベントモデルと 1.1 イベントモデルを混在させないことを推奨します。そうした場合の結果は予測不可であり、デバッグできない可能性があります。

コンポーネントのリスナ化

大半の 1.0 AWT 使用プログラムを 1.1 API に変換するとき、最も大きな仕事はイベント処理コードを変換することです。プログラムがどのイベントを処理するか、どのコンポーネントがイベントを生成するかを判断すれば、このプロセスは単純になります。1.1.1 Java コンパイラを使用している場合、-deprecation フラグ付でコンパイルすると、古いスタイルのイベント処理メソッドを含むリストが生成されます。(1.1.1 以前は、コンパイラが完全なリストを生成しなかったため、ソースファイルで "Event" を検索する必要がありました。) コード検索中に、どのクラスがイベント処理の目的のためだけに存在するかを注意しておく必要があります。そのようなクラスは削除ができます。

次に、1.0 コンポーネントをリスナに変換するときの手順を示します:

イベント処理のための内部クラスの使用

たくさんのメソッドを含む 1 つのインタフェースを実装する必要があるが、その内 1 つか 2 つのメソッドしか実装したくないとき、内部クラスは有効です。この節では、内部クラスにあまり深く立ち入らずに、それを使用してイベントハンドラを書く方法を説明します。 内部クラスの詳細については、内部クラスを参照してください。

イベント処理内部クラスの実装は、クラスにイベントリスナインタフェースを実装させる一種の変形です。以前と同様に、どのインタフェースをクラスが処理する必要のあるイベントに対応させるか判別します。しかし、インタフェースを実装する代わりに、そのインタフェースに対応しているアダプタを拡張する内部クラスを作成します。例えば、WindowListener を実装する代わりに、 WindowAdapter を拡張する内部クラスを実装できます。そして、単純に内部クラスのインスタンスを作成し、例えば addWindowListener メソッドで、それをリスナの適当な種類として登録します。次に例を示します:

public class MyClass ... {
    ...
    //Where the window is created:
        newWindow.addWindowListener(new MyInnerClass());
    ...
    class MyInnerClass extends WindowAdapter {
        public void windowClosing(WindowEvent event) {
            if (inAnApplet) {
                dispose();
            } else {
                System.exit(0);
            }
        }
    }
    ...
}

コンパイル後、内部クラスバイトコードは、次のような名前の別個な .class ファイルに置かれます: MyClass$MyInnerClass.class. 内部クラスを使用する完全な例としては、innerclass/DialogWindow.java を参照してください。

例: DialogWindow の変換

この節では、DialogWindow と呼ばれるプログラムの 1.0 API から 1.1 API への変換方法を説明します。 DialogWindow はアプリケーションですが、AppletButtonという名のクラスの支援を受けてアプレットとしても実行できます。 ( Java チュートリアル を読んだ方なら、 "Dialog の使い方"の節にもこの例が出てくるので、このプログラムを理解しやすいかもしれません。)

次に AppletButton の助けを借り、アプレットとして動作しているバージョン 1.0 の DialogWindow( ソースコードへのリンクがあります) を示します:


アプレットを実行できないので、ボタンで呼び出されるウィンドウ(Frame)のスナップショットが次にあります::

次には、そのウィンドウ用のダイアログのスナップショットがあります:


手順 1: コピーの作成

1.0 ソースとバイトコードを安全な場所に移動し、修正可能なソースファイルのコピーを 保持します。例えば、UNIX システム上では次のとおりです:
% mkdir 1.0example
% mv DialogWindow.class 1.0example
% cp DialogWindow.java 1.0example

手順 2: 自動的な変換

できる限りの自動変換を行います。例えば次のとおりです:
% updateAWT DialogWindow.java > tmp.java
% diff DialogWindow.java tmp.java
33c33
<         dialog.show();
---
>         dialog.setVisible(true);
38c38
<         textArea.appendText(text + "\n");
---
>         textArea.append(text + "\n");
47c47
<         window.show();
---
>         window.setVisible(true);
87c87
<         hide();
---
>         setVisible(false);
% mv tmp.java DialogWindow.java

手順 3: コンパイル

1.1 コンパイラを使用していることを確認し、DialogWindow をコンパイルします。例えば次のとおりです:
% which javac
/usr/local/java/jdk1.1.1/solaris/bin/javac
% javac DialogWindow.java
Note: DialogWindow.java uses a deprecated API.  Recompile with
"-deprecation" for details.
1 warning
%
% javac -deprecation DialogWindow.java
DialogWindow.java:18: Note: The method boolean handleEvent(java.awt.Event) in
class java.awt.Component has been deprecated, and class DialogWindow overrides
it.
    public boolean handleEvent(Event event) {
                   ^
DialogWindow.java:26: Note: The method boolean handleEvent(java.awt.Event) in
class java.awt.Component has been deprecated.
        return super.handleEvent(event);
                                ^
DialogWindow.java:29: Note: The method boolean action(java.awt.Event,
java.lang.Object) in class java.awt.Component has been deprecated, and class
DialogWindow overrides it.
    public boolean action(Event event, Object arg) {
                   ^
DialogWindow.java:81: Note: The method boolean action(java.awt.Event,
java.lang.Object) in class java.awt.Component has been deprecated, and class
SimpleDialog overrides it.
    public boolean action(Event event, Object arg) {
                   ^
Note: DialogWindow.java uses a deprecated API.  Please consult the
documentation for a better alternative.
5 warnings
%
ご覧になって分かるとおり、このクラスのコンパイルは成功していますが、推奨されないメソッド (handleEvent および action) を呼び出し、またはオーバーライドしています。

手順 4: 手作業による変更

コンポーネントのリスナ化で前述したように、DialogWindowプログラムにとって、この手順はイベント処理コードの変換だけからなっています。DialogWindow.java で "Event" を検索すれば、このプログラムを包む 2 つのクラス(DialogWindow および SimpleDialog)でイベント処理コードを見つけることができます。

次に DialogWindow プログラム内のイベント処理の概要を示します:

イベント処理コードの現在のロケーションは理に適ったものです。DialogWindow にすべてのイベントを処理させることもまた、それがこのアプリケーションの制御クラスであるという理由から、意味をもちます。さらに他の可能性としては、アクションイベントすべてを処理する非 Component 制御クラスを導入することも考えられます。 DialogWindow プログラムを変換するために、そのイベント処理コードを前と同じコンポーネントにおいて置き、コンポーネントにリスナインタフェースを実装させます。

次の例は、変換された DialogWindow プログラム内のイ ベント関連コードの最重要部分を示しています。顕著な変更は太字で示します。

import java.awt.event.*;

public class DialogWindow extends Frame 
			  implements WindowListener,
				     ActionListener {
    ...
    public DialogWindow() {
	...
        Button button = new Button("Click to bring up dialog");
	button.addActionListener(this);
	...
	addWindowListener(this);
    }

    public void windowClosed(WindowEvent event) {
    }

    public void windowDeiconified(WindowEvent event) {
    }

    public void windowIconified(WindowEvent event) {
    }

    public void windowActivated(WindowEvent event) {
    }

    public void windowDeactivated(WindowEvent event) {
    }

    public void windowOpened(WindowEvent event) {
    }

    public void windowClosing(WindowEvent event) {
        if (inAnApplet) {
            dispose();
        } else {
            System.exit(0);
        }
    }

    public void actionPerformed(ActionEvent event) {
        if (dialog == null) {
            dialog = new SimpleDialog(this, "A Simple Dialog");
        }
        dialog.setVisible();
    }
    ...
}

class SimpleDialog extends Dialog implements ActionListener {
    ...
    SimpleDialog(Frame dw, String title) {
	...
        field = new TextField(40);
	field.addActionListener(this);
	...
        Button b = new Button("Cancel");
	b.addActionListener(this);
        setButton = new Button("Set");
	setButton.addActionListener(this);
	...
    }

    public void actionPerformed(ActionEvent event) {
	Object source = event.getSource();
        if ( (source == setButton)
           | (source == field)) {
            parent.setText(field.getText());
        }
        field.selectAll();
        setVisible(false);
    }
}
WindowListener インタフェースを実装する代わりに、DialogWindow に、単純に WindowAdapter を拡張する内部クラスを含めることができます。次に内部クラスと共に実装された DialogWindow の最重要点を示します。1.0 バージョンの顕著な変更点は、太字になっています。
import java.awt.event.*;

public class DialogWindow extends Frame 
			  implements ActionListener {
    ...
    public DialogWindow() {
	...
        Button button = new Button("Click to bring up dialog");
	button.addActionListener(this);
	...
	addWindowListener(new DWAdapter());
    }

    class DWAdapter extends WindowAdapter {
        public void windowClosing(WindowEvent event) {
            if (inAnApplet) {
                dispose();
            } else {
                System.exit(0);
            }
        }
    }

    public void actionPerformed(ActionEvent event) {
        if (dialog == null) {
            dialog = new SimpleDialog(this, "A Simple Dialog");
        }
        dialog.setVisible(true);
    }
    ...
}

手順 5: テスト

DialogWindow プログラムはアプリケーションであるため、 Java インタプリタでこれを実行しテストします。例えば次のとおりです:
% java DialogWindow
DialogWindowアプリケーションを使用することによって、これがすべてのイベントを正しく処理しているかを調べることができます。


イベント変換表

次の表は、1.0 イベントを、対応する 1.1 のイベントに割り当てます。第 1 列には、イベントに固有のメソッド名(もしあれば)と共に、各々の 1.0 イベント型を示します。メソッドが記載されていないところでは、イベントは常に handleEvent メソッドで処理されます。第 2 列にはイベント型を生成できる 1.0 コンポーネントを示します。第 3 列には、リストアップされたイベントの 1.1 相当の処理を支援するリスナインタフェースを示します。第 4 列には各リスナインタフェースのメソッドを示します。

1.01.1
イベント/メソッド生成元インタフェースメソッド
ACTION_EVENT/action Button, List, MenuItem, TextField ActionListener actionPerformed(ActionEvent)
Checkbox, CheckboxMenuItem, Choice ItemListener itemStateChanged(ItemEvent)
WINDOW_DESTROY
WINDOW_EXPOSE
WINDOW_ICONIFY
WINDOW_DEICONIFY
Dialog, Frame WindowListener windowClosing(WindowEvent)
windowOpened(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(WindowEvent)
windowClosed(WindowEvent) (1.0 に相当するものなし)
windowActivated(WindowEvent) (1.0 に相当するものなし)
windowDeactivated(WindowEvent) (1.0 に相当するものなし)
WINDOW_MOVED Dialog, Frame ComponentListener componentMoved(ComponentEvent)
componentHidden(ComponentEvent) (1.0 に相当するものなし)
componentResized(ComponentEvent) (1.0 に相当するものなし)
componentShown(ComponentEvent) (1.0 に相当するものなし)
SCROLL_LINE_UP
SCROLL_LINE_DOWN
SCROLL_PAGE_UP
SCROLL_PAGE_DOWN
SCROLL_ABSOLUTE
SCROLL_BEGIN
SCROLL_END
Scrollbar AdjustmentListener
(または新しい ScrollPane クラスを使用)
adjustmentValueChanged(AdjustmentEvent)
LIST_SELECT
LIST_DESELECT
Checkbox, CheckboxMenuItem, Choice, List ItemListener itemStateChanged(ItemEvent)
MOUSE_DRAG/mouseDrag
MOUSE_MOVE/mouseMove
Canvas, Dialog, Frame, Panel, Window MouseMotionListener mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
MOUSE_DOWN/mouseDown
MOUSE_UP/mouseUp
MOUSE_ENTER/mouseEnter
MOUSE_EXIT/mouseExit
Canvas, Dialog, Frame, Panel, Window MouseListener mousePressed(MouseEvent)
mouseReleased(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mouseClicked(MouseEvent) (1.0に相当するものなし)
KEY_PRESS/keyDown
KEY_RELEASE/keyUp
KEY_ACTION/keyDown
KEY_ACTION_RELEASE/keyUp
Component KeyListener keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent) (1.0に相当するものなし)
GOT_FOCUS/gotFocus
LOST_FOCUS/lostFocus
Component FocusListener focusGained(FocusEvent)
focusLost(FocusEvent)
1.0に相当するものなし ContainerListener componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
1.0に相当するものなし TextListener textValueChanged(TextEvent)


AWT の拡張の目次へ戻る
By Kathy Walrath