クラス変数を利用するに当たり、コードの可読性と明りょう性を改善するために、「staticインポート」を使うことができます。使い方は簡単で、利用したいクラス変数に対して「import static」を付けます。こうすると、クラス変数を使うに当たり、クラス名を省略できます。
次の例では、Integerの最小値から最大値まで、int型の値を1増加させて、その値をほかのint型の値へ代入するという処理をしています。この計算に費やしたミリ秒を表示しているので、Javaのバージョン、OS、CPUを変更して同じプログラムを動作させてみると面白いかもしれません。
package sample11.app11; import static java.lang.Integer.MIN_VALUE; import static java.lang.Integer.MAX_VALUE; class App { public static void main(String[] args) { long startTime = System.currentTimeMillis(); int j; for (int i = MIN_VALUE; i < MAX_VALUE ; i++) { j = i; } long result = System.currentTimeMillis() - startTime; System.out.println(System.getProperty("java.version")); System.out.println(System.getProperty("os.name")); System.out.println(result); } }
App.loadedTime:2009/09/19 18:29:55 count:1 L:2009/09/19 18:29:55 I:2009/09/19 18:29:56 count:2 L:2009/09/19 18:29:55 I:2009/09/19 18:29:57
当たり前のことですが、staticインポートを利用することにより混乱しそうな場合は使わないようにしましょう。単に「クラス名をタイプしないで済ませたい」といった理由で、staticインポートを使うと、利用しているクラス変数がどういうものなのか分からなくなるので、避けた方がよいでしょう。
例えば、System.outをstaticインポートしたりすると、outと書くだけでSystem.outを利用できますが、クラス内でoutという変数名を使いたくなるメソッドが出てきたときに、困ることになるはずです。
インスタンスを初期化するに当たり、複雑な計算結果を使ってフィールドの初期化をしたい場合があります。そんなときは、Javaでは「初期化ブロック」というものを使います。初期化ブロックは、コンストラクタの前に実行されるので、その中にフィールドの初期化処理をコーディングします。
package sample11.app9; import java.text.Format; import java.util.Date; import java.text.DateFormat; class App { String initTime; static int count = 0; {// 初期化ブロック ここから // インスタンス変数の複雑な初期化 Date currentDate = new Date(); Format dft = DateFormat.getDateTimeInstance(); initTime = dft.format(currentDate); // クラス変数の変更も可能 count++; System.out.println("count:"+count); }// 初期化ブロック ここまで void exec() { System.out.println(initTime); } public static void main(String[] args) throws Exception { App app = new App(); // 1つめのインスタンス生成 app.exec(); Thread.sleep(1000); // 1000ミリ秒一時停止 app = new App(); // 2つめのインスタンス生成 app.exec(); } }
ここでは、Appクラスのインスタンスを生成した際に、その時刻をinitTimeへ文字列として保存し、クラス変数のcountフィールドをカウントアップするという処理をしています。また、mainメソッド内では、「Thread.sleep(1000);」という処理で1000ミリ秒(1秒)一時停止をして、時刻がずれるようにしています。
count:1 2009/09/19 18:28:30 count:2 2009/09/19 18:28:31
クラス変数は、インスタンスがなくても参照ができます。先ほどの例では、インスタンス変数の複雑な初期化とクラス変数の値変更とを実行できましたが、クラス変数の複雑な初期化は含まれていませんでした。クラス変数の複雑な初期化をするためには、「static初期化ブロック」を使います。次の例では、クラス変数のloadedTimeを初期化するstatic初期化ブロックを用意しました。
package sample11.app10; import java.text.Format; import java.util.Date; import java.text.DateFormat; class App { static String loadedTime; String initTime; static int count = 0; // static 初期化ブロック static { Date currentDate = new Date(); Format dft = DateFormat.getDateTimeInstance(); loadedTime = dft.format(currentDate); } {// 初期化ブロック ここから Date currentDate = new Date(); Format dft = DateFormat.getDateTimeInstance(); initTime = dft.format(currentDate); count++; System.out.println("count:"+count); }// 初期化ブロック ここまで void exec() { System.out.print("L:" + loadedTime); System.out.println("\tI:" + initTime); } public static void main(String[] args) throws Exception { // Appクラスをロードするために、App.loadedTimeへアクセス System.out.println("App.loadedTime:" + App.loadedTime); Thread.sleep(1000); // 1000ミリ秒一時停止 App app = new App(); // 1つめのインスタンス生成 app.exec(); Thread.sleep(1000); // 1000ミリ秒一時停止 app = new App(); // 2つめのインスタンス生成 app.exec(); } }
「new Date()」により、現在時刻の情報を持つDate型インスタンスが生成されます。この情報を文字列表現へ変換して、loadedTimeへ代入しています。使用しているクラスの詳細については、APIリファレンスなどを参考にしてください。ここでは、こういった複雑な手順でクラス変数を初期化していることを理解できれば十分です。
実行結果からは、クラス変数は「2009/09/19 18:29:55」で初期化されていて、この値はインスタンス生成とは関係なく初期化された値のままであることが分かります。また、先に生成されたインスタンスのinitTime(インスタンス変数)の値は「2009/09/19 18:29:56」、後に生成されたインスタンスのinitTimeの値は「2009/09/19 18:29:5」であることが分かります。
App.loadedTime:2009/09/19 18:29:55 count:1 L:2009/09/19 18:29:55 I:2009/09/19 18:29:56 count:2 L:2009/09/19 18:29:55 I:2009/09/19 18:29:57
今回は、クラス変数、クラスメソッド、キーワードstatic、static初期化、staticインポートについて解説をしました。キーワードstaticを使うことにより、クラスで共有するフィールドやメソッドが宣言でき、便利になる場面があることを理解できたでしょうか。また、JavaのラッパクラスやSystemクラスにあるクラス変数やクラスメソッドの利用方法について理解できたでしょうか。
JavaのAPIリファレンスを見ても分かるように、クラス変数、クラスメソッドはよく利用されています。大変便利ですから、使いこなせるようによく理解してください。
クラス変数やクラスメソッドを宣言するようになると、static初期化を使いたくなるはずです。利用する機会は多くはないと思いますが、頭の隅に入れておいてください。staticインポートも必須の文法事項ではありませんが、うまく利用すればコードが読みやすくなります。使いすぎに注意して、適切に利用できるようになりましょう。
また、初心者のうちは、クラス変数やクラスメソッドを知ると、何でもこれらを使うように実装してしまう傾向があります。インスタンスメソッドを使った方法は、コーディング量が多くなるからです。しかし、クラスメソッドはクラスで共有されてしまうために、使いにくい場面もあります。
クラス変数、クラスメソッドを使う場合、フィールドやメソッドについて、「クラス共有で用意するべきものなのか」「インスタンスごとに別で利用できるようにするべきものなのか」ということをきちんと設計時に検討するようにしましょう。クラス変数やクラスメソッドを使えるようになることで、プログラミングの幅は広がりますが、適切なクラス設計となるよう、メソッドの役割、クラスの役割をよく考えて設計と実装をしないと、使いにくいクラスが出来上がってしまいます。注意しましょう。
次回は、いよいよ継承について解説をします。「インターフェイス」の回「プログラムを「変更」しやすくする“インターフェイス”」でも継承の話は出ましたが、今度は実装の継承について詳しく説明をします。お楽しみに。
今回作ったサンプルのソースコードはこちらからダウンロードできます。
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)やbugs(J)の活動へも参加している。
著書に「基礎Java」(インプレス)、共著に「Javaコレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。
Copyright © ITmedia, Inc. All Rights Reserved.