イベントリスナーの実装 |
Javaでは、イベントリスナーを実装するのに3つの方法があります。そしてそれぞれ3つの方法には、
長所と短所があります。ソースの可読性を高める上では、1つの方法を全体を通して使うべきですが、
あくまで個人的なプログラムであれば、多少読みづらくても楽に作れた方がいいこともあります。
ここでは3種類のイベントリスナーの実装方法について説明します。
|
|
方法1
|
1つ目の方法はアプリケーションのコンテナクラスにインタフェースを実装する方法です。
恐らくこれが最もオーソドックスなものだと思います。以下のソースを見てください。
import java.awt.*;
import java.awt.event.*;
class TestFrame extends Frame implements ActionListener {
Button button = new Button();
...
public void TestFrame {
button.addActionListener(this);
...
}
public void actionPerformed(ActionEvent e) {
...
}
}
|
コンテナであるFrameクラスのサブクラスにActionListenerインタフェースを実装しています。
この方法だと、このコンテナがイベントリスナーを実装していることが明確になります。
クラス宣言を見ただけで、何らかのイベントを実装しているんだと分かりますね。
ただし、この方法には短所もあります。それは、例えばいくつものボタンを使って
いくつもの処理を行う際には、actionPerformedメソッドの中でActionEvent#getSourceメソッドを
使ってどのボタンが押されたのか調べることになりますが、ボタンの数が多いとこのメソッドが肥大しがちです。
そうなると当然読みづらいものになってしまいます。
そこで、それぞれのボタンに対して別のリスナーを登録する方法があります。
それが2番目の方法です。
|
|
方法2
|
2つ目の方法は無名内部クラスを使う方法です。以下のソースを見てください。
import java.awt.*;
import java.awt.event.*;
class TestFrame extends Frame {
Button button = new Button();
...
public void TestFrame {
button.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
...
}
}
);
...
}
}
|
この方法だと、それぞれのコンポネーントごとに実装を分離できます。
そのため、「このボタンはこういう処理をするんだな」という事がより明確になります。
方法1を使うとactionPerformedメソッドが大きくなりすぎる場合は有効かも知れません。
ただし、見てもらえれば分かるように、明らかに全体のプログラムは大きくなり、
また、それぞれのコンポーネントに上のような記述を施すのは面倒です。
ところで、方法1、方法2は両方とも、ActionListenerという名前のインタフェースを実装しています。
インタフェースを実装するということは、そのインタフェースで定義されている抽象メソッドを
全て実装しなければならないということです。
上の例ではActionListenerインタフェースなので、定義すべきメソッドはactionPerformed1つだけ
でしたが、WindowListenerや、MouseListenerではもっとたくさんのメソッドが定義されているので、
たとえその内1つのメソッドしか使わなくても、すべてのメソッドを実装しなければなりません
(具体的には、空のメソッドとなります。ああ、めんどくさい。)。これは正直面倒ですし、
メソッドを書くのを忘れてコンパイルエラー、なんてこともよくあります。
そこで、必要なメソッドだけを使ってイベントリスナーを実装できる方法があります。
それが方法3です。
|
|
方法3
|
3つ目の方法ではアダプタクラスを使います。以下のソースを見てください。
import java.awt.*;
import java.awt.event.*;
class TestFrame extends Frame {
...
public void TestFrame {
addMouseListener(
new MouseAdapter() {
public void mousePressed(MouseEvent e) {
...
}
}
);
...
}
}
|
この方法の明らかな長所は、必要なメソッドだけを実装すればいいことです。
上記のMouseAdapterに対応するインタフェースであるMouseListenerでは、5つのメソッドが
抽象メソッドとして定義されているので、5つのメソッド全てを実装しなければなりません。
ところがアダプタクラスを使えば、必要なmousePressedメソッドだけを実装すればいいのです。
この事からも明らかなように、アダプタクラスは抽象クラスです。
インタフェースでは抽象メソッドしか定義できず、それを実装したクラスはすべての抽象メソッドを
実装しなければなりません。けれども抽象クラスでは、抽象メソッドと実装されたメソッドを混在させる
ことが可能です。以下はMouseAdapterクラスのコードです(Javadocコメント、コメント、package宣言は
省略しています)。
/*MouseAdapter.java*/
public abstract class MouseAdapter implements MouseListener {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
|
上記のように、アダプタクラスでは対応するインタフェースを実装し、デフォルトの処理
(何もしない)を記述することで、そのサブクラスに実装の義務を与えません。
なので、全てのメソッドをオーバーライドする必要はなく、
自分の実装したいメソッドだけをオーバーライドすることが出来ます。
ちなみに、無名内部クラスというのは、implementsやextendsの省略形だと考えることが出来ます。
当然のことですが、インタフェースや抽象クラスは、インスタンスを生成することが出来ないので、
方法2や方法3のコード中の無名内部クラスは、それぞれActionListenerの実装クラスと、
MouseAdapterのサブクラスだと考えられます。
|
|