さて、こうして書いてみると、初期値や、開始時刻といった値の表示について担当するクラスと、カウントダウンをするクラスは別々にして、もう少しすっきりとした構成にしたくなってきます。そんなときは、クラスを別に作成をすればいいのですが、普通にクラスを作成すると、別ファイルで用意する必要が出てきます。
Eclipseを使えば、別ファイルで用意すること自体は、それほど手間でもありませんが、これから作成するsample16.App3クラスにしか関係ないクラスがsample16パッケージ内にあると、クラス間の関係がファイルを見ただけでは分からない状態となります。sample16.Appクラス、sample16.App2クラスとは別にしたいところですが、それだけのためにパッケージを用意するのも大げさな気がします。
こんなときは、ネストした型の1つであるメンバ・クラスを利用します。これまで、クラスのメンバとしては、フィールドやメソッドがあることを説明しましたが、クラスやインターフェイスもメンバとすることができます。メンバ・クラスは、クラスの内部にクラスを宣言することで実装できますし、メンバ・インターフェイスも同様です。
ここでは、sample16.App3クラスの内部にクラスを宣言して利用することにします。先にApp2クラスをコピーして、App3クラスを作成します。それから、ネストしたクラスをsample16.App3クラスの内部に作成します。
例えば、sample16.App3クラスの内部にネストしたクラスとしてProgressViewクラスを作成するとします。Eclipseでは、sample16.App3クラスを作ってから、次のようにします。
それでは、カウント中の表示をするProgressViewクラスと、結果を表示するResultViewクラスを上記手順を参考にして用意してください。出来上がったら、sample16.App3クラスを次のようにコーディングします。sample16.App2から変更した行には「//」を付けておきました。このまま写すと、Eclipse上ではエラーとなって赤色の下線がたくさん付きますが、この時点ではそれで構いません。
package sample16; public class App3 { // ProgressViewクラス class ProgressView { // 略 } // ResultViewクラス class ResultView { // 略 } final static long INTERVAL = 1000; ResultView resultView = new ResultView(); // ProgressView progressView = new ProgressView(); // int count; // // コンストラクタ App3() { count = 12; resultView.setStartValue(count); } public void execute() throws Exception { resultView.setStartTime(System.currentTimeMillis()); // while (count > 0) { Thread.sleep(INTERVAL); count--; progressView.countUp(1); // progressView.update(); // } resultView.setStopTime(System.currentTimeMillis()); // resultView.update(); // } public static void main(String[] args) throws Exception { App3 app = new App3(); app.execute(); } }
sample16.App3のメンバとして追加されたものは、ProgressViewクラス、ResultViewクラス、resultViewフィールド、progressViewフィールド、countフィールドです。App3のコンストラクタも用意しています。executeメソッド内にあった変数countは、コンストラクタで初期化して、executeメソッドで利用するので、フィールドに移動しています。
ProgressViewクラスは、カウントした値をcountUpメソッドで指定してから、updateメソッドで出力するようにします。ResultViewクラスは、「startTime」「startValue」「stopTime」フィールドを用意して、それぞれへのアクセサメソッドを用意します。結果出力用には、updateメソッドを用意します。表示の部分がProgressViewクラスやResultViewクラスの役割として分担されるので、sample16.App3クラスのコードが分かりやすくなりました。
それでは、ProgressViewクラスを実装しましょう。「progressView.countUp(1);」の上にマウスカーソルを置いてしばらく待つと、エラーとクイックフィックスが表示されます(図4)。ここで、クイックフィックスの「型'ProgressView'のメソッド'countUp(int)'を作成します」を指定します。
「resultView.update();」も同様にします。こうすると、ProgressViewクラス内にメソッドの宣言が追加されるので、次のように編集します。
countUpメソッドについては、仮引数名がiとなって生成されましたが、メソッド内のカウンタ用変数でiを使いたかったので、valueを短縮したvに変更している点に注意してください。
class ProgressView { int count = 0; String printValue = ""; public void countUp(int v) { count += v; StringBuilder sb = new StringBuilder(); for (int i = 0; i < v; i++) { sb.append("."); } printValue = sb.toString(); } public void update() { System.out.print(printValue); if (count % 10 == 0 && count != 0) { System.out.println(""); } } }
改行を出力する際にsample16.App2では若干複雑な処理をしていましたが、こちらではcountフィールドを用意して、カウントアップされた数を記録しておくことにより、「countの値が10で割り切れて0でないときに改行をする」という分かりやすい実装になっています。一方で、countUpメソッドのvには、表示する「.」の数が指定されるので、もともとのsample16.App2クラスでの処理よりも複雑になっています。
次に、ResultViewクラスを実装します。こちらは、先にstartTime、stopTime、startValueをフィールドとして宣言してます。次に下記の手順でアクセサメソッドを生成します。
次に、updateメソッドを追加します。Eclipseの機能で自動で生成されるメソッドにはpublicが付きますが、メソッドをpublicにする必要はありません。ここでは、updateメソッドにはpublicを付けないで実装してみます。
updateメソッド内の処理は単に値を画面出力しているだけなので、特に説明は必要しなくても理解できるでしょう。
class ResultView { public long getStartTime() { return startTime; } public void setStartTime(long startTime) { this.startTime = startTime; } public long getStopTime() { return stopTime; } public void setStopTime(long stopTime) { this.stopTime = stopTime; } public int getStartValue() { return startValue; } public void setStartValue(int startValue) { this.startValue = startValue; } long startTime; long stopTime; int startValue; void update() { System.out.println(""); System.out.println("開始時刻:" + startTime); System.out.println("カウント:" + startValue); System.out.println("終了時刻:" + stopTime); } }
エラーがなくなったら、プロジェクトと対応するフォルダの中(連載では「C:\workspace\eclipse\Sample\」)を見てみましょう。「bin」フォルダ内にクラスファイルが生成されています。sample16パッケージは「bin」フォルダ内の「sample16」フォルダに対応するので、そのフォルダをファイルエクスプローラで開いて確認してみましょう(図5)。
作成したメンバ・クラスは、「App3$ProgressView.class」と「App3$ResultView.class」という名前のファイルで生成されています。つまり、これらがApp3と関係してるクラスだということが分かるように、クラス名の前に「App3$」が自動的に付くようになっているので、覚えておきましょう。
「$」はこのように使われるので、自作のクラスで「$」を含むクラス名を指定することは推奨されていませんが、使えないわけではありません。例えば、「Name$Sample」という名前のクラスを作成することはできます。興味がある読者は、試しにEclipseで作って確認してみるといいでしょう。
sample16.App3を実行すると、sample16.App2の実行結果と同様の出力がされます。どうでしょう。メンバ・クラスを使うと、関係が深いクラスと一緒にまとめておくことができるので、便利だと思いませんか。
ただし、むやみやたらと使うこともできません。該当するクラスに属する必要のないクラスを宣言して使うこともできてしまいますから、「本当に、そのクラスはネストしたクラスとする必要があるのか」よく考えて使いましょう。
次ページでは、メンバ・インターフェイスの使い方も解説します。
Copyright © ITmedia, Inc. All Rights Reserved.