staticなメソッドはインスタンスと無関係という特徴があります。インスタンスメソッドは、そのインスタンスの情報(インスタンスフィールド)の値を利用して何か処理を行う場合がほとんどです。言い換えれば、インスタンスを予約語 newによって生成した後に、はじめてそのインスタンスに付属するインスタンスメソッドを利用できるということになります。それに対して、staticなメソッドはインスタンスとは無関係に利用することができます。言い換えれば、「staticなメソッドの中では予約語 thisを使用できない」ということでもあります。thisは、「自身のインスタンス」を表す言葉ですから。
以下では、大きく 2つの例について見ていきます。
ひとつ目の例として、既存の(インスタンスメソッドが幾つもある)クラスに staticなメソッドが同居している例を挙げてみます。以下では、これまでの Dogクラスに、このクラスの説明を表示するための printHelpメソッドを追加しています。
ソースコードは以下の通り。
L201/Dog.java
/** * 犬を表すクラスです。 */ public class Dog { /** * 名前。 */ private String name; /** * 鳴く回数。 */ private int cryCount; /** * コンストラクタ * @param name 名前 */ public Dog(String name) { this(name, 1); } /** * コンストラクタ。 * @param name 名前 * @param cryCount 鳴く回数 */ public Dog(String name, int cryCount) { this.setName(name); this.setCryCount(cryCount); } /** * 名前を設定します。 * @param name 名前 */ public void setName(String name) { this.name = name; } /** * 鳴く回数を設定します。 * @param cryCount 鳴く回数 */ public void setCryCount(int cryCount) { this.cryCount = cryCount; } /** * 鳴きます。 */ public void cry() { System.out.print(this.name + "「"); for(int i = 0; i < this.cryCount; i ++) { System.out.print("ワン"); } System.out.println("」"); } /** * お座りします。 */ public void sitDown() { System.out.println(this.name + "は座りました。"); } /** * ヘルプメッセージを表示します。 */ public static void printHelp() { System.out.println("これは犬のクラスです。"); System.out.println("犬インスタンスは、名前の設定と鳴く回数の設定と"); System.out.println("鳴くことと座らせることができます。"); } }
L201/L201.java
/** * staticなフィールドを確認します。 */ public class L201 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { // 犬のstaticなメソッドを実行 Dog.printHelp(); // インスタンスを作成 Dog dog1 = new Dog("ポチ"); Dog dog2 = new Dog("クロ", 5); Dog dog3 = new Dog("ラッキー", 3); // 犬のインスタンスメソッドを実行 dog1.cry(); dog1.sitDown(); dog2.cry(); dog3.cry(); } }
実行結果は以下の通り。
これは犬のクラスです。 犬インスタンスは、名前の設定と鳴く回数の設定と 鳴くことと座らせることができます。 ポチ「ワン」 ポチは座りました。 クロ「ワンワンワンワンワン」 ラッキー「ワンワンワン」
まず、数を数えられることが大切です。Dogクラスについて、以下の数を正確に数えられるでしょうか。
printHelpメソッドの内部では、Dogクラスのインスタンスフィールドやインスタンスメソッドを必要としません。インスタンスとは無関係な処理内容ということで staticなメソッドとして実装しています。これによって、インスタンスを作成することなく(予約語 newを使用することなく)printHelpメソッドを呼び出すことができています。
もうひとつの例として「staticなメソッドしかないクラス」を作成してみます。
いわゆるライブラリと呼ばれるものになります。1から指定した数までの和を求める sumメソッドと、以前作成した factorialメソッドを持つ MyMathクラスを作成することにしました。
ソースコードは以下の通り。
L202/MyMath.java
/** * 数学ライブラリです。 */ public class MyMath { /** * プライベートなコンストラクタ。 */ private MyMath() { ; } /** * 1から指定した数までの合計を返します。 * 0以下の値を指定しないで下さい、注意。 * @param number 数 * @return 合計値 */ public static int sum(int number) { int sum = 0; for(int i = 1; i <= number; i ++) { sum += i; } return sum; } /** * 指定した数の階乗を求めます。なお、0以下の値を指定した場合、1を返します、注意。 * @param number 数 * @return 階乗の値 */ public static long factorial(int number) { long value = 1; for(int i = number; i >=1 ; i --) { value *= i; } return value; } }
L202/L202.java
/** * MyMathクラスを利用します。 */ public class L202 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { System.out.println("1~10までの和は " + MyMath.sum(10)); System.out.println("1~100までの和は " + MyMath.sum(100)); System.out.println("1~10までの積は " + MyMath.factorial(10)); } }
実行結果は以下の通り。
1~10までの和は 55 1~100までの和は 5050 1~10までの積は 3628800
まず、数を数えられることが大切です。MyMathクラスについて、以下の数を正確に数えられるでしょうか。
MyMathの sumメソッドも factorialメソッドも staticなメソッドなので、メインメソッド側ではインスタンスを生成することなく(予約語 newを使用することなく)メソッドを利用することができています。
なお、9行目から 11行目に privateなコンストラクタを記述していることについては、後の章で改めて解説することにします。コンストラクタについてまったく記述しないと、デフォルトコンストラクタが補完されるのでした。ということは、以下のような記述で、MyMath型インスタンスを生成することができてしまいます。
MyMath myMath = new MyMath();
ところが、MyMathクラスは、インスタンスを生成してどうのこうのするクラスではありません(宣言されたインスタンスフィールドも宣言されたインスタンスメソッドもゼロです)。そのため、インスタンスを生成できないようにするために、コンストラクタに予約語 privateを使用して、外部から newできないようにしています。Javaで用意されている既存のクラスにおいても、以前利用したことがある Mathクラス、その他に Systemクラスなども、newによってインスタンスを生成することができないようになっています。