staticの罠
このレッスンでは、static とインスタンスの間で何を参照できて何を参照できないのかを判定できるようになります。
まず予測してください
class Counter {
static int created;
int id;
static String report() {
return "created=" + created + ",id=" + id; // ここは通る?
}
}答えはコンパイルエラーです。static メソッドの中から、インスタンスフィールドである id を名前だけで書くことはできません。
向きによって可否が変わる
この規則は片側通行です。表にすると次のようになります。
| 見る側 | 見られる側 | 可否 |
|---|---|---|
| static メソッド | static フィールド | 可 |
| static メソッド | インスタンスフィールド | 不可 |
| インスタンスメソッド | static フィールド | 可 |
| インスタンスメソッド | インスタンスフィールド | 可 |
理由は所属先の違いです。static メンバはクラスに1つだけ存在し、インスタンスが1つも作られていなくても使えます。一方インスタンスメンバは「どのインスタンスの分か」が決まらないと値が定まりません。static メソッドはその「どの」に当たる情報を持っていないので、書けないのです。
直し方は2つ
- インスタンスを引数で受け取る —
static String report(Counter c)にしてc.idと書く - インスタンスメソッドにする —
staticを外せば自分自身のidを見られる
どちらでも動きますが、試験では1つ目の形で「引数で受け取れば触れる」ことを確かめさせる出題が多いです。
this と super も static では使えない
同じ理由で、static メソッドの中では this と super も書けません。「対象のインスタンスがない」という一点から、すべてが説明できます。逆にインスタンスメソッドから static フィールドを触るのは自由です。むしろ this.created のようにインスタンス経由でも書けてしまうので、読み手が static だと気づきにくい書き方として出題されることがあります。
static は共有されている
もう1つの定番が、static フィールドがすべてのインスタンスで共有されている点です。
Counter a = new Counter(); // created は 1
Counter b = new Counter(); // created は 2
System.out.println(a.created); // 2 が出るa.created と書いても、実体は Counter.created 1つきりです。インスタンスごとに別の値を持っているように見せる選択肢が用意されるので、引っ掛からないようにしてください。
演習で確かめます
コンパイルできない report を、引数でインスタンスを受け取る形に直します。
要件
- report は Counter を引数で受け取り、created と id を created=3,id=3 の形でつないで返すこと
- check の先頭で Counter.created を 0 に戻すこと
- check は Counter を n 個作り、最後に作ったインスタンスを report に渡した結果を返すこと
入出力例
check(3) → "created=3,id=3"
check(1) → "created=1,id=1"
check(5) → "created=5,id=5"ヒント
編集 LuaGate編集部