JFrame継承クラスにimplementsする
今度は、アクションイベントが発生したら、JFrameに組み込んであるコンポーネントを操作して処理を実行させてみることにしましょう。JTextFieldに書いてあるテキストを使って、JLabelに表示をしてみます。
そのために、作成したActionAdapterのactionPerformedメソッドを書き換えて、
public void actionPerformed(ActionEvent ev){ String s = field.getText(); label.setText("あなたは、" + s + "と書きました。"); }
このようにしてみました。結果はどうでしょう? これは、コンパイルエラーになってコンパイルできないはずです。なぜなら、fieldやlabelといったコンポーネントがあるクラスと、actionPerformedメソッドがあるクラスは別のクラスだからです。fieldやlabelはSampleクラスのフィールドですから、AcrtionAdapterクラスでは、そのままでは利用できないのです。
では、どうすればよいのでしょうか。この解決法はいろいろと考えられます。まず、ActionAdapterの中に、Sampleインスタンスを保管するフィールドを用意しておけばいいんじゃないか、と思う人も多いことでしょう。Sampleインスタンスは、newする際に使用するインスタンスを受け渡すようにしておけば、うまくいきそうです。
package codezine.java; import java.awt.event.*; import javax.swing.*; public class Sample extends JFrame { JLabel label; JTextField field; JButton button; public Sample(){ this.setSize(300,200); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLayout(null); // JLabelの組み込み String msg0 = "<html><h3>Sample</h3></html>"; label = new JLabel(msg0); label.setSize(200,30); label.setLocation(20,20); this.add(label); // JTextFieldの組み込み field = new JTextField(); field.setSize(200,20); field.setLocation(20,50); this.add(field); // JButtonの組み込み button = new JButton("JButton"); button.setSize(100,25); button.setLocation(20,80); button.addActionListener(new ActionAdapter(this)); this.add(button); } public static void main(String[] args) { Sample sample = new Sample(); sample.setVisible(true); } } class ActionAdapter implements ActionListener { Sample sample; public ActionAdapter(Sample s){ this.sample = s; } public void actionPerformed(ActionEvent ev){ String s = sample.field.getText(); sample.label.setText("あなたは、" + s + "と書きました。"); } }

ここでは、ActionAdapterクラスの中にSampleインスタンスを保管するsampleフィールドを用意しておきました。そしてインスタンスを作成する際にコンストラクタで引数のSampleをフィールドに保管しておきます。ActionAdapterのコンストラクタを見てみましょう。
public ActionAdapter(Sample s){ this.sample = s; }
このように、引数で渡されたSampleインスタンスをフィールドに保管しています。sampleフィールドは、単純にsampleと書くだけでもいいんですが、「このインスタンスの中にあるsampleですよ」ということがわかるように、this.sampleと書いてあります。インスタンス内のフィールドは、こんな具合にして指定できるのですね。
そして、actionPerformedでは、このsampleに収めたインスタンスを利用してコンポーネントを操作しています。これは、sample内のフィールドのメソッドを呼び出す、という形になります。
String s = sample.field.getText(); sample.label.setText("あなたは、" + s + "と書きました。");
sampleフィールドの中にはSampleインスタンスが収めてあります。この中のfieldやlabelフィールドのメソッドを呼び出して、コンポーネントを扱っているのです。sample.field.getTextというように、ちょっと長ったらしい書き方となってしまいますが、これでメソッドを呼び出すことはできるのです。
後は、このActionAdapterのインスタンスを作成するときにSampleインスタンスを引数に指定するように修正するだけです。
button.addActionListener(new ActionAdapter(this));
thisは、このインスタンス自身を示すものでした。これで、Sampleインスタンス自身を引数にしてActionAdapterインスタンスが作成されます。そして、作成したActionAdapterのsampleフィールドには引数のthisが設定され、このthisにあるコンポーネントを操作するようになる、というわけです。
JFrame継承クラスにimplementsする
今の方法では、JFrameのインスタンスを引数に渡したり、フィールドにインスタンスを保管して操作するためのソースを大幅に修正しなければいけなくなります。これではちょっと面倒です。もっと手軽で簡単に利用する方法はないのでしょうか。
最も広く用いられているのは、SampleクラスそのものにActionListenerをimplementsしてしまう、というものです。わざわざ独立したクラスを作らずに、Sampleに兼用させてしまえばよい、という考え方です。
package codezine.java; import java.awt.event.*; import javax.swing.*; public class Sample extends JFrame implements ActionListener { JLabel label; JTextField field; JButton button; public Sample(){ this.setSize(300,200); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLayout(null); // JLabelの組み込み String msg0 = "<html><h3>Sample</h3></html>"; label = new JLabel(msg0); label.setSize(200,30); label.setLocation(20,20); this.add(label); // JTextFieldの組み込み field = new JTextField(); field.setSize(200,20); field.setLocation(20,50); this.add(field); // JButtonの組み込み button = new JButton("JButton"); button.setSize(100,25); button.setLocation(20,80); button.addActionListener(this); this.add(button); } public void actionPerformed(ActionEvent ev){ String s = field.getText(); label.setText("あなたは、" + s + "と書きました。"); } public static void main(String[] args) { Sample sample = new Sample(); sample.setVisible(true); } }
見れば分かるように、Sampleクラスがclass Sample extends JFrame implements ActionListenerという形になっています。implementsは、extendsで継承をしているクラスでも組み込むことができます。こうやってSampleクラス自身に組み込んでしまえば一番簡単です。
ボタンにaddActionListenerしている部分を見ると、button.addActionListener(this);というようになっています。thisはこのインスタンス自身を示すものでした。これで、自分自身をアクションリスナーとして組み込むことができます。
この方法は、ちょっとしたイベント処理を用意するのに最も手軽な方法です。ただし、利用するイベントリスナーが多くなってくると、1つのクラスの中に一切合財詰め込むというやり方はあまりお勧めできなくなります。1つのクラスがやたらと複雑になってしまっては、プログラムも読みにくくなりますし、後々のメンテナンスもやりにくくなるものです。なにより、この方法では1つのイベントリスナーについて1つしかクラスを定義できません。例えば複数のボタンに複数のActionListenerを設定する、というような場合は、このやり方は使えません。
