広告

■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
                      2010年01月06日

    Java総合講座 - 初心者から達人へのパスポート
                  2009年11月開講コース 008号

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■□■□■□■□■□■□■□■□■□■□■□■□■□■□■


-------------------------------------------------------
・現在、このメールマガジンは以下の2部構成になっています。
[1] 当初からのコース:毎週日曜の夜に発行
   これは現在、中級レベルになっています。
[2] 2009年11月開講コース:毎週水曜の夜に発行
   これは現在、初心者向けのレベルになっています。
・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
(2009年11月開講コース)008号
 (当記事はvol.008のリバイバル(revival)版です。)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


========================================================
◆ 01.ウィンドウの表示
========================================================

今回は、簡単なウインドウのアプリケーションを作ってみましょう。

ウインドウを使って、HumanTheaterで行っていたのと同じように
Humanオブジェクトの名前を表示するアプリケーションを作ってみる
ことにします。


Javaではウインドウ、メニュー、ボタンなどのいわゆるGUI
(Graphical User Interface)の部品群もあらかじめ提供されて
います。

その部品群のひとつがAWTです。

AWTは、Abstract Windowing Toolkitの略で、複数の(異種の)OS
の持つGUIの共通部分を抽出して作ったクラス群です。

このおかげで、たとえばWindowsでもUnixのXウインドウ・システムでも
同じように動作するウインドウ・アプリケーションを作ることができます。

しかし、異なるOS間のGUIの共通部分を抽出したのでは、部品数が少なく
て表現力に乏しいなどの欠点がありますので、現在では、Swingと呼ばれる、
もっと豊富な部品群が別途提供されています。

AWTがOSのGUI機能に依存しているのに対し、SwingはOSに依存せずに
Javaで独自にGUIの部品群を作っていますので、OSに依存せずに表現力
豊かなウインドウのアプリケーションを作ることができます。

しかし、今回作るアプリケーションは説明を簡単に済ませるために、
AWTのほうを採用することにします。(Swingを使ったアプリケーショ
ンの開発方法については、また後ほどお話いたします。)



通常のアプリケーションのウィンドウはフレーム・ウインドウ(frame window)
と呼ばれるもので、AWTでもずばりFrameというクラスによって提供されます。
これはパッケージjava.awtに入っています。

しかし、単純にFrameをそのまま使うのではなく、何らかの拡張を行って使う
ために、Frameのサブクラスを作って使います。

ここでは、HumanTheaterクラスと同じことをするFrameということで、Frameの
サブクラスの名前をずばりHumanTheaterFrameにしようかとも思ったのですが、
長いのでやめにして、HumanFrameという名前にします。

さてHumanFrameのオブジェクトに以下のような作業をやらせたいと思います。
(1)タイトル・バーにHuman Theaterと表示させる。
(2)ウインドウのサイズを縦100ピクセル、横300ピクセルにする。
(ピクセルとは、いわゆる画素のことで、画像の最小単位です。)

プログラム上の記述方法はいくつかありますが、たとえば、以下のような方法があります。

HumanFrame frame = new HumanFrame(); // HumanFrameのインスタンスを生成
frame.setTitle("Human Theater"); // タイトル・バーのタイトルを設定
frame.setSize(300, 100); // ウインドウのサイズを横300ピクセル、縦100ピクセルにする

さて、HumanTheaterクラスと同じ様に、HumanFrameにも、「What is your name?」
「My name is Hanako.」という会話を表示させたいので、文字列を表示させるため
のGUIの部品を追加しましょう。

その一つが、Labelというクラスです。

「What is your name?」用に一つと「My name is Hanako.」用に一つ、合計二つ
のLabelオブジェクトを用意することにします。

Label label1 = new Label();
Label label2 = new Label();

以上でLabelのインスタンスを2つ生成し、それぞれlabel1とlabel2という変数
に代入しました。

これらをHumanFrameのインスタンスに追加しましょう。ウインドウの上にGUI部品
を追加するにはadd( )というメソッドを使います。

クライアント領域とよばれる、ウインドウのお腹の部分にGUI部品を貼り付ける
ことになりますが、この部分には、LayoutManager(レイアウト管理者)と呼ば
れるオブジェクトを張り付けて、レイアウトを管理させることになっています。

そして、デフォルト(標準)ではBorderLayoutというLayoutManagerが張り付い
ています。

┌補足─────────────────────────┐
詳しくいうと、LayoutManagerはインターフェイス(interface)と
呼ばれるもので、メソッドの名前など、呼び出し方(シグニチャー
(signature)という)を規定したものです。詳しくは後述します。

一方、BorderLayoutはそのメソッドなどを実際にプログラミングし
たクラスになっています。この「実際にプログラミングすること」
を「実装」(implement)といいます。
└───────────────────────────┘

BorderLayoutというLayoutManagerは、GUI部品を上・下・左・右・中央の合計
5個所に貼り付けられるようにします。
そのうち、上はNORTH、下はSOUTH、左はWEST、右はEAST、中央はCENTERと呼び
ます。

では、label1をNORTHに、label2をSOUTHに配置させることにしましょう。
そのためには、add( )メソッドの呼び出しを次のように書きます。

frame.add(label1, BorderLayout.NORTH);
frame.add(label2, BorderLayout.SOUTH);

NORTHやSOUTHは、実際にはBorderLayoutクラスのstatic変数として定義されて
いますので、上記のような指定の仕方になります。

次にHumanオブジェクトを生成して、これまでのように名前を答えさせましょう。

ただし、「What is your name?」「My name is Hanako.」という会話文は
System.out.println( )を使って標準出力に表示するのではなく、Labelのインス
タンスに表示させますので、LabelのsetText( )というメソッドを使って会話文を
入力しておきます。

Human human = new Human(1970, 8, 1);
human.setName("Hanako");
label1.setText("What is your name?");
label2.setText("My name is " + human.getName() + ".");

最後の2行のようにそれぞれのLabelオブジェクトのsetText( )メソッドで文字列
を設定しておくと、Labelオブジェクトがそれを表示してくれます。

準備がととのったら、ウィンドウ全体を表示します。

frame.setVisible(true);

ここでsetVisible( )は可視性を設定するメソッドで、true(真)を設定すると
可視状態になり、false(偽)を設定すると不可視状態になります。
可視状態にするということは、要するに表示することを意味します。


ところで、我々がウインドウを操作するときには、メニューを選択したり、ボタン
をクリックするなどの事象が発生しますが、この「事象」のことを英語でevent
(イベント)といいます。

現在のJavaでは、こういった事象(イベント)をEventListener(イベント・リスナー)
というオブジェクトを通して、特定のオブジェクトのメソッド呼び出しに変換する仕組み
が提供されています。
これは、一種のデザイン・パターンと呼ばれる技術に基づく、オブジェクト指向的な
仕組みなのですが、詳しいことは後述します。

イベント・リスナーというと事象の聞き手というような意味になりますが、事象を聞く
という表現はちょっとへんですから、事象の監視者という意味だと理解しておいたほう
がわかりやすいかもしれません。


今回は、「閉じる」ボタンをクリックした時のイベントの処理について説明します。

ウインドウを閉じるときの処理はとても大切なことです。
たとえば文書を編集しているときに誤って「閉じる」ボタンをクリックしてしまったら、
文書が保存もされずにアプリケーションが終了してしまった、なんてことになると困り
ますね。
何も言わずに勝手にウインドウが閉じてしまっては困ります。

そこで、Javaでも、「閉じる」ボタンをクリックしたら勝手にウインドウを閉じるなん
てことはしません。
「閉じる」ボタンをクリックしたら何をするか、ということは自分でプログラミングし
ないといけないことになっているのです。


これらをふまえて記述したHumanFrameのソース・コードの例を下に記します。

----------------------------------------------
package jp.co.flsi.lecture.ui;  // 1行目

import java.awt.BorderLayout;  // 2行目
import java.awt.Frame;  // 3行目
import java.awt.Label;  // 4行目
import java.awt.event.WindowEvent;  // 5行目
import java.awt.event.WindowListener;  // 6行目
import jp.co.flsi.lecture.human.Human;  // 7行目

public class HumanFrame extends Frame {  // 8行目
   HumanEventListener listener = new HumanEventListener();  // 9行目

   class HumanEventListener implements WindowListener {  // 10行目
      public void windowActivated(WindowEvent e) {};  // 11行目
      public void windowClosed(WindowEvent e) {  // 12行目
         System.exit(0);  // 13行目
      };  // 14行目
      public void windowClosing(WindowEvent e) {  // 15行目
         Frame frame = (Frame)e.getSource();  // 16行目
         frame.setVisible(false);  // 17行目
         frame.dispose();  // 18行目
      };  // 19行目
      public void windowDeactivated(WindowEvent e) {};  // 20行目
      public void windowDeiconified(WindowEvent e) {};  // 21行目
      public void windowIconified(WindowEvent e) {};  // 22行目
      public void windowOpened(WindowEvent e) {};  // 23行目
   };  // 24行目

   public HumanFrame()  {  // 25行目
      super();  // 26行目
      addWindowListener(listener);  // 27行目
   }  // 28行目

   public static void main(String[] args) {  // 29行目
      HumanFrame frame = new HumanFrame();  // 30行目
      frame.setTitle("Human Theater");  // 31行目
      frame.setSize(300, 100);  // 32行目 setSize(幅, 高さ)
      Label label1 = new Label();  // 33行目
      Label label2 = new Label();  // 34行目
      frame.add(label1, BorderLayout.NORTH);  // 35行目
      frame.add(label2, BorderLayout.SOUTH);  // 36行目
      Human human = new Human(1970, 8, 1);  // 37行目
      human.setName("Hanako");  // 38行目
      label1.setText("What is your name?");  // 39行目
      label2.setText("My name is " + human.getName() + ".");  // 40行目
      frame.setVisible(true);  // 41行目
   }  // 42行目
}  // 43行目
----------------------------------------------

1行目から順番に見ていきましょう。

package jp.co.flsi.lecture.ui;  // 1行目

もうおなじみのpackage文です。
今回のクラスは、GUI(Graphical User Interface)のクラスということで、
User Interfaceの略のuiをパッケージ名の最後に付けることにし、
jp.co.flsi.lecture.uiという名前のパッケージにHumanFrameクラスを入れる
ことにしました。

次に2行目から7行目まで

import java.awt.BorderLayout;  // 2行目
import java.awt.Frame;  // 3行目
import java.awt.Label;  // 4行目
import java.awt.event.WindowEvent;  // 5行目
import java.awt.event.WindowListener;  // 6行目
import jp.co.flsi.lecture.human.Human;  // 7行目

ですが、これらは今回使用するクラスのimport文です。例によって、Eclipseの
コンテンツ・アシスト機能で自動的に入力されますから、細かい説明は省略します。

ただ、一つだけ注意して欲しいことがあります。

今回もHumanクラスを使います(7行目参照)が、HumanFrameクラスとHumanクラス
では、パッケージが異なります。つまり、HumanFrameクラスはjp.co.flsi.lecture.ui
に入っているのに対し、Humanクラスはjp.co.flsi.lecture.humanに入っています。

このように他のパッケージのクラスを参照する場合にはそのクラスにpublic指定がなけ
ればなりません。
つまり、Humanクラスの定義時に

public class Human

というように、class文にpublicの指定をしておかなければならないことになっています。

そして、通常は、特別な理由がない限り、classにはpublicを指定するのが普通です。

したがって、Humanのclass文をpublic指定に変えておきましょう。

Humanだけでなく、今後定義するクラスは特に断らない限りpublicを指定するものと
します。


次に8行目

public class HumanFrame extends Frame {  // 8行目

のextends Frameという記述は、以前にも説明したようにHumanFrameというクラスが
Frameのサブクラスであることを宣言しています。


次の9行目

   HumanEventListener listener = new HumanEventListener();  // 9行目

は、その後の10行目から24行目までに定義しているイベント・リスナーのクラス
HumanEventListenerのインスタンスを生成してlistenerという変数に代入して
いるものです。

10行目〜24行目はイベント・リスナー(EventListener)のクラス定義ですが、
HumanFrameのクラス定義の内部に組み込まれていることに注意してください。

こういうのをinner class(内部クラス)とかnested class(ネストされたクラス、
入れ子にされたクラス)といいます。

(正確にいうと、nested classにはinner classでないものもあります。staticが
指定されたnested classがそれです。詳しいことは後述します。)

HumanFrame専用のイベント・リスナーなのでHumanEventListenerという名前にしま
したが、こういう場合はHumanFrameと別個に定義する必要がないので、HumanFrameの
内部に定義してしまうのが普通なのです。

このnested classの場合に注意して欲しいことは、クラス定義の最後(24行目)や
その中のメソッドなどの定義の最後(11行目、14行目、19行目、20行目、21行目、
22行目、23行目)にも、文の終わりのマーク(;)を入れることです。
この点が普通のクラス定義と異なります。


10行目

class HumanEventListener implements WindowListener {  // 10行目

のimplementsは実装するという意味のキーワードです。
その後ろのWindowListenerはウインドウ用のイベント・リスナーで、これもやはり
インターフェイスと呼ばれるものです。

10行目の記述は、HumanEventListenerがWindowListenerというインターフェイス
を実装したクラスであることを意味します。

そしてその実装の内容が11行目〜23行目です。


11行目

public void windowActivated(WindowEvent e) {};  // 11行目

のwindowActivated( )というメソッドは、ウインドウがアクティブになった
(いわゆるフォーカスがあたった状態のことで、この状態だとタイトル・バーが
青色になっている)時に呼び出されるものですが、ここでは何の処理もしていません。

なお、引数のWindowEvent型のオブジェクトは、発生したイベントの情報を持つオブ
ジェクトです。


12行目〜14行目

public void windowClosed(WindowEvent e) {  // 12行目
   System.exit(0);  // 13行目
};  // 14行目

のwindowClosed( )というメソッドは、ウインドウが閉じたあとで呼び出される
メソッドで、13行目のSystem.exit( )は、JVM(Java Virtual Machine)を終了
させる操作を示します。
このアプリケーションのウインドウが閉じてしまったら、もはやJVMが動いている必要は
ありませんから、このような指示をしておきます。なお、exit( )の引数は、アプリケー
ションが正常終了ならば0を指定します。


15行目〜19行目

public void windowClosing(WindowEvent e) {  // 15行目
   Frame frame = (Frame)e.getSource();  // 16行目
   frame.setVisible(false);  // 17行目
   frame.dispose();  // 18行目
};  // 19行目

のwindowClosing( )というメソッドは「閉じる」ボタンをクリックしたときに呼び
出されますので、ここでウインドウを閉じる操作を行っておく必要があります。

16行目のe.getSource()は、イベントの発生元を取り出すためのメソッド呼び出しです。
その前についている(Frame)という記述は「キャスト」と言って、その後ろに指定した
オブジェクトの型を変換することを指示するものです。
ここでは、e.getSource()が返してきたオブジェクトの型をFrameに変換しています。

このHumanEventListenerというイベント・リスナーはHumanFrameという(そのスー
パークラスが)Frame型のオブジェクトにつないで使うことが予めわかっているので、
イベントの発生元もFrame型であることがわかっているわけです。それで、(Frame)という
キャストによってFrame型に変換させるのです。
そして、そのイベントの発生元であるFrameオブジェクトをframeという変数に代入して
います。

なお、16行目で宣言したframeという変数は、19行目の}で閉じられるブロック内でしか
通用しません。

一般に変数には適用できる範囲(scope)というものがあり、ブロック内で宣言された
変数はそのブロック内でしか通用しないことになっています。
もしそのブロックの外に同名の変数があった場合は、その変数は名前は同じでも、別の
変数として扱われます。


17行目では、このFrameオブジェクトを不可視の状態に変更しています。
そして18行目では、このFrameオブジェクトを消滅させています。こうすると、この
Frameオブジェクトのために確保されていたメモリー上の記憶域も開放されます。

こうしてウインドウが閉じると先ほどの12行目〜14行目が実行され、JVMが終了させられ
るわけです。

次の20行目〜23行目については説明を省略します。これらも何の処理もしていません。

      public void windowDeactivated(WindowEvent e) {};  // 20行目
      public void windowDeiconified(WindowEvent e) {};  // 21行目
      public void windowIconified(WindowEvent e) {};  // 22行目
      public void windowOpened(WindowEvent e) {};  // 23行目


次の25行目〜28行目

   public HumanFrame()  {  // 25行目
      super();  // 26行目
      addWindowListener(listener);  // 27行目
   }  // 28行目

はHumanFrameのコンストラクターの定義です。

26行目のsuper()というのは、スーパークラスのコンストラクターを呼び出すことを
意味します。
スーパークラス(ここではFrame)のコンストラクター(ここではFrame())がやって
いたことはそのままスーパークラスのコンストラクターにやらせれば済む話なので、
このような呼び出しを行います。

27行目は、HumanFrameのオブジェクトにイベント・リスナーを登録するためのメソッド
呼び出しです。
ここでは、9行目で生成したイベント・リスナーのインスタンスを登録しています。

登録が何を意味するのか、知りたい人は、HumanFrameのインスタンスを
HumanEventListenerのインスタンスと糸電話か電話線でつなぐようなイメージでも
頭に浮かべてください。

こうやってFrameのインスタンスにイベント・リスナーを登録しておくと、Frameオブ
ジェクト上で(「閉じる」ボタンをクリックするなどの)イベントが発生したときに、
Frameオブジェクトがイベント・リスナーに(糸電話あるいは電話線を通して)イベント
の情報を伝えてくれます。イベントの情報を伝えるというのは、具体的には11行目〜23
行目のメソッドを呼び出してくれるということです。
イベントの情報は、これらのメソッドの引数という形で渡され(伝えられ)ます。


次の29行目からは、main( )メソッドの定義です。30行目以降は先ほど説明したもの
なので、もう説明する必要はないでしょう。

ただ、ちょっとわかりにくいところが一点ありますね。

30行目ではHumanFrameのインスタンスを生成していますが、このようにHumanFrameク
ラスの定義の中でHumanFrameのインスタンスを生成するのは、頭をこんがらせる話かも
しれません。

このようにJavaでは、クラス定義の中でそのクラスを使用する(いわゆる、再帰的に
使用する)ことは、許されているのですが、さらにmain( )メソッドがstaticメソッド
(static指定のメソッド)であることに注目してください。

以前もお話したように、staticメソッドは、インスタンスとは関係しません。つまり、
インスタンスとは独立した存在なので、staticメソッドの中で同じクラスのインスタンス
を生成したり、そのインスタンスにメッセージを送ったりすることには何も問題がないの
です。

これを理解しやすくするためには、「クラスによって表現されるオブジェクトにはインス
タンス以外にもstatic変数とstaticメソッドで構成されるオブジェクトがあり、これは
インスタンスのオブジェクトとは別物である」と考えてください。


以上でHumanFrameのソース・コードが理解できたことと思います。


それでは次回は、ここまでの内容をEclipseに入力してウインドウの表示を試してみましょう。



================================================
◆ 02.演習問題
================================================

上記のイベント・リスナーの登録

      addWindowListener(listener);  // 27行目

を、HumanFrameのコンストラクター以外の場所で行うように
プログラムを変更してみてください。



======================================================
◆ 03.前回の演習問題の答
======================================================

1.
たとえば、やはり会話風に表示することにして

package jp.co.flsi.lecture.human;

class HumanTheater {
   public static void main(String[] args) {
      Human hanako = new Human(1970, 8, 1);
      hanako.setName("Hanako");
      System.out.println("What is your name?");
      hanako.sayName();
      System.out.println("How old are you?");
      System.out.println("I am " + hanako.getAge() + " years old.");
   }
}

という感じのプログラムにしてみるのが一つの答ですね。


2.
dataOfBirthと同じようなやり方にするのが一つの方法ですね。



◆編集後記◆━━━━━━━━━━━━━━━━━━━━━━━━

☆最後まで読んでくださってありがとうございます。

☆次回は、HumanFrameを少し対話的なアプリケーションに変えて
 みようと思います。
お楽しみに。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2010 Future Lifestyle Inc. 不許無断複製