概要
Javaの「static」には5つの種類がある。しかし、それぞれは似て非なるものである。
本記事では、それぞれのstaticについて、落とし穴にハマらないための要点を説明する。
もっと詳しく知りたい場合は、記事最後の参考資料などを参照されたい。
1. staticメソッド
メソッドにstatic修飾子が付与されていると、staticメソッドとなる。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
staticメソッドは、インスタンスをnewしなくても呼び出すことができるため、一見便利に思える(使いやすそうに見える)。
しかし、単体テストを書きやすくするためには、「決まりきった処理」のみに利用すべきである。
2. staticフィールド
フィールドにstatic修飾子が付与されていると、staticフィールドとなる。
public static final double PI = 3.14159265358979323846;
上記のように、final修飾子と一緒に利用することで、定数として利用することができる。
final修飾子と一緒に利用することで、プログラムのどこかで値を書き換えられ、誤った計算結果を出力してしまうなどのバグを防ぐことができる。
なお、システムの設計によっては、キャッシュの保持などにも利用することができる。
3. 【中級者向け】staticインポート
外部のクラスのstaticフィールドや staticメソッドを、クラス名を指定せずに呼び出すことができるようになる。
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class SampleTest {
@Test
public void Test() {
assertThat("foo", is("foo"));
}
}
上記のサンプルでは、Assert.assertThat
やMatchers.is
について、クラス名を省略して記述することができている。
4. 【中級者向け】staticインナークラス
インナークラスにstatic修飾子が付与されていると、staticインナークラスとなる。
public class Outer {
public static class Inner {
// staticインナークラス
}
}
非staticなインナークラスは親クラスのフィールドやインスタンスメソッドにもアクセスできる。
このため、親クラスへの参照を保持している。
しかし、不必要な親クラスへの参照は、メモリやGCへの悪影響1が懸念される。
インナークラスをstaticにできる状況であれば、staticにすべき2である。
5. 【中級者向け】staticイニシャライザ
下記のブロック内に記載した処理は、そのクラスに初めてアクセスされる時に呼び出される。
static {
// 処理
}
具体的には、staticフィールドのコレクションの初期化などに利用する3。
public static final List<String> LIST;
static {
List<String> tmp = new ArrayList<>();
tmp.add("foo");
tmp.add("bar");
LIST = Collections.unmodifiableList(tmp);
}