クラスメソッドとクラス変数
これまで、メソッドと変数は全てクラスに属し、インスタンスを生成してから使用するものと学んできました。
クラスメソッドとクラス変数はインスタンスを生成せずに使うことができます。
クラスメソッド
クラスメソッドはstaticメソッド(静的メソッド)とも呼ばれクラスに属するメソッドであり、メソッドにstaticを付けて宣言します。
public class Foo { public static void method() { // 処理 } }
static修飾子が付く以外は通常のメソッドと同じで、引数と戻り値を持つことが出来ます。
クラスメソッドを使用する場合、次のようにしてクラス名から直接起動します。
Foo.method();
次のように記述してもコンパイルエラーとはなりませんが正しいとは言えません(Eclipse上では警告となります)。
Foo foo = new Foo();
foo.method();
極端な話をすればクラスメソッドのみでほとんどのプログラムは記述できます。
しかも、インスタンスを生成する手間もなく便利と考えると大きな落とし穴があります。
オーバーライド
クラスメソッドはオーバーライドできません。
正確に言えば同じシグニチャのメソッドをサブクラスで定義(オーバーライド)できますが、それぞれのメソッドは全く別のメソッドと言えます。
次のようにサブクラスを作ったとしてもBarはFooを継承している為、Bar.method()は実行できます。
public class Bar extends Foo { }
しかし、次のようにメソッドをオーバーライドした場合、Bar.method()とFoo.method()は全く別のメソッドとなります。
public class Bar extends Foo { public static void method() { // 処理 } }
通常のメソッドではインスタンスの型に依存して実行メソッドが決まります。
しかし、クラスメソッドはインスタンスには依存しない為、実行されるクラスメソッドは対象としたクラス(またはそのスーパークラス)に属しているに過ぎません。
クラス変数
クラスメソッドと同じように変数にstatic修飾子を付与することでクラスに属する変数を定義することが出来ます。
このような変数をクラス変数(static変数)と呼びます。
public class Foo { // クラス変数 public static int size = 10; // クラスメソッド public static void method() { // 処理 } }
クラス変数はクラスメソッドと同様にクラス名から直接参照できます。
int i = Foo.size;
クラス変数はクラスに属する為、クラスメソッドから参照することができます。
public class Foo { // クラス変数 private static int size = 10; // クラスメソッド public static int getSize() { return size; } }
インスタンスメソッドとクラスメソッド
クラスメソッド(staticメソッド)に対して、通常のメソッドはインスタンスメソッド(非staticメソッド)と呼ばれます。
これまで学んできたように、インスタンスメソッドはインスタンスを介してのみ実行可能です。
クラスメソッドはインスタンスの有無に関わらず使用できる為、インスタンスメソッドの中でも使うことができます。
public class Foo { // クラスメソッド public static void method() { // 処理 } // インスタンスメソッド public void foo() { method(); } }
このように同一クラス内ではクラス名を省略することが可能です。
しかし、クラスメソッド内でインスタンスメソッドを使うことはできません。
public class Foo { // クラスメソッド public static void method() { foo(); // コンパイルエラー } // インスタンスメソッド public void foo() { } }
何故ならば、クラスに直接属しているクラスメソッドはインスタンスとは無関係だからです。
変数に関しても同様のことが言えます。
すなわち、インスタンスメソッドからクラス変数を参照することはできますが、クラスメソッドからインスタンス変数を参照することはできません。
メソッド | 実行(参照) | 可否 |
---|---|---|
インスタンスメソッド | インスタンスメソッド | ○ |
クラスメソッド | ○ | |
インスタンス変数 | ○ | |
クラス変数 | ○ | |
クラスメソッド | インスタンスメソッド | × |
クラスメソッド | ○ | |
インスタンス変数 | × | |
クラス変数 | ○ |
publicなクラス変数
publicなクラス変数はプログラムのどんな場所からも参照・代入が可能となります。
このような変数はグローバル変数と呼ばれ、C言語の時代では最もバグを混入しやすい箇所でした。
何故ならば、どんなタイミングでその変数が呼ばれ更新されるのかを管理できない為、問題が生じたときに原因を突き止めるのが困難になるからです。
publicなクラス変数は原則として記述してはなりません。
リテラル
publicなクラス変数は原則として記述してはならないのですが、変数が代入されないと保障されるのであれば問題はありません。
むしろ、各クラスにインスタンス変数を作るよりもメモリの効率も良いでしょう。
そんな時にはfinal修飾子を変数に用います。
// デフォルトサイズ:10 public static final int DEFAULT_SIZE = 10;
final修飾子をつけた変数は1度初期化された後はコンパイラレベルで代入を抑止され、事実上はRead Onlyとなりリテラル(定数)として扱えます。
Javaでは慣習としてリテラルは全て大文字で記述し、単語区切りをアンダーバーで表現します。
finalで宣言されたクラス変数は変更されることがない為、デフォルト値などの固定値としてよく用いられます。
ユーティリティメソッド
クラスメソッドはクラスに属しインスタンスに無関係です。
これは便利のようにも思えますが、Java(オブジェクト指向プログラミング)はクラスのインスタンスを中心にプログラムが構成されるのであり、異端とも言える記法です。
mainメソッドのようにプログラムの実行のキーとなるメソッド(エントリーポイント)などインスタンスに属さないほうが便利な場合がある為に言語仕様として採用されています。
どのような場合にクラスメソッドを使用しても問題ないかと言えば、いわゆるユーティリティメソッドなどを実装するときです。
ユーティリティメソッドとは1回のメソッド呼び出しで処理が完結し、それ以上拡張性も考えにくい不変的なメソッドと言えます。
例えば、整数の絶対値を計算するメソッド「int abs(int num)」は引数で与えた整数が正であればそのままの値を返し、負であれば-1を掛けた値を返すでしょう。
このメソッドの仕様はこれ以上でもこれ以下でもなく、整数と絶対値という概念が変わらない限りは変わりません。
単純な計算クラスにこのメソッドをインスタンス変数として定義することも出来ますが、おそらく計算クラスで実装される他のメソッドも同じように不変的な計算を行うでしょう。
それらは全てユーティリティメソッドとなり、計算クラスはユーティリティクラスと呼べる事になります。
public class Calc { // 整数の絶対値を取得 public static int abs(int num) { if(num < 0) { return (-1) * num; } return num; } }
しかし、このメソッドを計算機クラスに実装し計算機クラスのインスタンス変数に対する操作になるならばインスタンスメソッドが適当になるでしょう。