“ネスト”した型で始める軽量Javaプログラミング!?【改訂版】Eclipseではじめるプログラミング(16)(1/3 ページ)

これからプログラミングを学習したい方、Javaは難しそうでとっつきづらいという方のためのJavaプログラミング超入門連載です。最新のEclipse 3.4とJava 6を使い大幅に情報量を増やした、連載「Eclipseではじめるプログラミング」の改訂版となります

» 2010年05月20日 00時00分 公開
[小山博史株式会社ガリレオ]

通常のJavaプログラミングよりも“軽量”に

 プログラミング言語Javaでは、1つのクラスを1ファイルに記述することを基本としています。そのため、プログラムを作成する際に、いくつかのクラスが必要になると、複数のファイルを用意することになります。

 再利用性やプログラムの保守性を考えると、ファイル別になっているのは便利な面が多いのですが、ちょっとしたプログラムを作成する際には、1つのクラスごとに1ファイルを用意してコーディングすることが、逆に面倒で「保守性が悪くなる」と感じることがあります。

 そんなときは、ネストしたクラスを利用してみましょう。ネストしたクラスを使うと、少しずつ動作を確認しては、拡張していくアジャイル(俊敏)な開発をするのが楽になり、“通常のJavaプログラミングよりもライトウェイト(軽量)なプログラミング”ができます。ちょっとしたアイデアを実現してみるプロトタイプ作成や、いろいろなライブラリサンプルプログラム作成などに役に立ちます。

 EclipseでJavaプログラミングを始める準備がまだの方は、連載第1回の「Eclipse 3.4で超簡単Javaプログラミング基礎入門」で準備をしておいてください。

ネストした型(クラス/インターフェイス)とは

 これまでの連載では、クラスインターフェイスを独自に作成する場合には、対応するファイルを用意して、そこにコーディングをすることを説明してきました。実は、Java言語ではクラスやインターフェイスの中でも、クラスやインターフェイスを宣言できます。コードのブロックの中でも、これらを宣言できるのです。

 このように、ある型の内部で宣言されたクラスを「ネストしたクラス(nested classes)」といいます。同様に、ある型の内部で宣言されたインターフェイスを「ネストしたインターフェイス(nested interfaces)」といいます。ネストしたクラスと、ネストしたインターフェイスの両方を合わせて「ネストした型(nested types)」といいます。

ネストした型を囲む型は「エンクロージング型」

 ネストした型を囲む型のことを「エンクロージング型(enclosing type)」といいます。ネストした型には、用途に応じて「static」を付けたり、付けなかったりします。

トップレベルのクラス/インターフェイス

 なお、ネストしたクラスやインターフェイスに対して、ネストしていないクラスやインターフェイスのことを「トップレベルのクラスやインターフェイス」といいます。

 今回はネストした型とはどういうものかを理解するために、クラス内で宣言するネストしたクラスとインターフェイスを使ったプログラミングについて説明します。

サンプル「タイマーアプリ」概要

 文法的な説明を中心にするより、具体的な例を見ながらの方が、ネストした型について理解しやすいでしょう。ここからは簡単なタイマーを作りながら、ネストしたクラスやインターフェイスを使ってみることにしましょう。

 サンプルとして、1秒ごとに1カウントダウンするプログラムを作ることにします。まずは、12秒という固定タイマーで実装してみます。

Threadクラスを使う

 プログラムを1000ミリ秒一時停止するためには、java.lang.Threadクラスのstaticメソッドであるsleepメソッドを呼び出します。Threadクラスについて詳細は説明しませんので、ここでは、こう書けばプログラムを1000ミリ秒一時停止できると理解しておいてください。

Thread.sleep(1000);

タイマー起動クラス

 「sample16」パッケージを作成して、起動クラス「App」を作成します。Appクラスでは、1000ミリ秒を表すために「INTERVAL」というstaticな定数を用意しています。「execute」メソッドの中で、1秒間プログラムを一時停止してはcountの値を1減らしているので、これで12秒タイマーとなります。executeとmain、それぞれのメソッドに「throws Exception」が指定されている点に注意をしてください

package sample16;
 
public class App {
    final static long INTERVAL = 1000;
    public void execute() throws Exception {
        int count = 12;
        while (count > 0) {
            Thread.sleep(INTERVAL);
            count--;
        }
    }
    public static void main(String[] args)
    throws Exception {
        App app = new App();
        app.execute();
    }
}
sample16/App.java

Eclipseで実行構成を指定して実行

 コンパイルエラーが出ていないことが確認できたら、sample16.Appクラスを実行してみます。Eclipseでは、プログラムの実行時のコマンドパラメータを指定したり、実行環境を指定できるようになっています。今回は、実行構成を指定して実行してみましょう。

 sample16.Appクラスをマウスの右ボタンでクリックして表示されるメニューで[実行]→[実行の構成]をクリックします。表示された画面で、引数JREの指定ができることが分かります。ここでは、[名前]に「sample16.App」を指定して[実行]をクリックします(図1)。

図1 [実行構成]ダイアログ 図1 [実行構成]ダイアログ

 実行してみると、Eclipseの下側にある[コンソール]タブで、12秒ぐらいで「<終了> App [Java アプリケーション(略)」といった表示がされて、プログラムは終了します(図2)。

図2 [コンソール]タブの表示 図2 [コンソール]タブの表示

 コンソールの出力画面には何も表示されないので、これだけでは、本当にプログラムが動いているのか分かりません。改良してみましょう。

タイマーアプリに出力機能を追加

 カウントダウン中は1秒ごとに「.」を表示し、終了後にカウントダウンを始めた時刻、カウントダウンが終了した時刻、カウントダウンした秒数、カウントダウンの初期値を表示してみます。区切りが分かるように、10秒ごとに改行を入れるようにしています。基本的には、System.outのprintメソッドや、printlnメソッドを使っているだけなので、処理の内容自体に難しい点はないはずです。

Eclipseでクラスをコピー

 次の手順でAppをコピーして、App2を作成します。こうすると、クラス名がApp2に変更され、mainメソッド内でAppクラスを使っている部分もApp2クラスに自動で変更されます。

  1. Eclipseでsample16の「App.java」を選択
  2. マウスの右ボタンをクリックして表示されるメニューで[コピー]を指定
  3. パッケージのsample16をマウスの右ボタンでクリックして表示されるメニューで[貼り付け]を指定
  4. [名前の競合]ダイアログで['App'の新規名を入力してください:]にApp2を入力して[OK]をクリック

 App2クラスを次のように変更します。カウントの値を保持する変数としてstartValue、開始時刻を保持する変数としてstartTime、終了時刻を保持する変数としてstopTimeを用意しています。

package sample16;
 
public class App2 {
    final static long INTERVAL = 1000;
    public void execute() throws Exception {
        int count = 12;
        int startValue = count;
        long startTime = System.currentTimeMillis();
        while (count > 0) {
            Thread.sleep(INTERVAL);
            count--;
            System.out.print(".");
            boolean isNewLine =
            ((startValue - count) % 10) == 0;
            if (isNewLine && count != 0) {
                System.out.println("");
            }
        }
        long stopTime = System.currentTimeMillis();
        System.out.println("");
        System.out.println("開始時刻:" + startTime);
        System.out.println("カウント:" + startValue);
        System.out.println("終了時刻:" + stopTime);
    }
    public static void main(String[] args)
    throws Exception {
        App2 app = new App2();
        app.execute();
    }
}
sample16/App2.java

 「.」の出力については、1秒間一時停止した後に出力しています。ただし、だらだらと出力しても数が分からなくなるので、10回「.」を出力したら改行を入れることにして、一時停止するごとに改行が必要かどうかチェックをしています。

 具体的には、「startValue - count」が10で割り切れる場合、「(startValue - count) % 10」の値は「0」になります。ですから、「(startValue - count) % 10 == 0」を計算してisNewLine変数へ代入し、これが「true」のときに改行するようにしています。

System.currentTimeMillis()で時刻を表現

 時刻の表現方法には、文字列で持つ方法、「java.util.Calendar」型で持つ方法、「java.util.Date」型で持つ方法、long型で持つ方法などなど、いろいろあります。どれかの型に決めてしまってもいいのですが、ここではSystem.currentTimeMillis()の返却値をそのまま持つことにしています。

 この値は、Java Platform SE 6の「System」によると、「ミリ秒で測定した、現在時刻と協定世界時の UTC 1970 年 1 月 1 日午前 0 時との差」です。

出力機能を確認

 プログラムができたら、sample16.Appを実行したときと同様にして、sample16.App2を実行してみましょう。

..........
..
開始時刻:1272757288039
カウント:12
終了時刻:1272757300067
App2.javaの実行結果例

 この結果を見ると、「1272757300067-1272757288039=12028(ミリ秒)」ですから、大体12秒のカウントダウンができています。また、「.」の出力について10回で改行、12回出力を実現できていることが分かります。

 次ページでいよいよ、このタイマーアプリにネストしたクラスを適用させます。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。