今回やること
staticとfinalは役割が違います。staticはクラス全体で共有するため、finalは一度代入した参照や値を再代入しないために使います。
学校のメンバーを表すクラスで、次の3つを確認します。
- 全メンバーで共有する作成人数:
static - 作成後に変えない個人ID:
final - 全員が共通で使う学校名:
static final
| 宣言 | 意味 | 呼び出し方 |
|---|---|---|
static int count | クラスに1つの共有値 | SchoolMember.count |
final int id | 初期化後に再代入しない値 | member.id |
static final String SCHOOL_NAME | クラス共通の定数 | SchoolMember.SCHOOL_NAME |
オブジェクトを2つ作った時の個数を比べます。
| メンバー | 通常のフィールド | staticフィールド |
|---|---|---|
| 1人目 | nameとidを1組持つ | countを共有する |
| 2人目 | 別のnameとidを持つ | 同じcountを見る |
staticは「どこからでも自由に使える」という意味ではありません。誰が変更してよいか分かりにくい共有状態は、増やしすぎないことが大切です。
1. staticで人数を共有する
通常のフィールドはオブジェクトごとに別々です。staticフィールドは、どのオブジェクトから見ても同じ1つの値です。
static int count = 0;
コンストラクタでcountを増やすと、何人作られたかをクラス全体で数えられます。
staticメソッドもインスタンスなしで呼び出せます。例えばMath.max(3, 5)はMathオブジェクトを作らずに使います。一方、showInfo()は各メンバーのnameとidを使うため、インスタンスメソッドです。
2. finalでIDを固定する
final int id;
finalフィールドは、コンストラクタで一度だけ代入できます。その後にid = 99と再代入するとコンパイルエラーです。
宣言時に値を決められない場合でも、すべてのコンストラクタが必ず一度代入すればfinalフィールドを使えます。この例では作成人数を増やした直後の値をIDにしています。
ただし、finalは「オブジェクトの中身がすべて不変」という意味ではありません。ここでは数値IDを変えない用途に限定します。
3. static finalで定数を作る
全員に共通し、途中で変更しない学校名は定数にします。
static final String SCHOOL_NAME = "Ada High School";
定数名はSCHOOL_NAMEのように、大文字とアンダースコアで書く慣例があります。
文字列をコード中へ何度も直接書く代わりに定数へまとめると、意味と変更箇所が明確になります。ただし、環境ごとに変わるURLや秘密情報はコード定数にせず、設定や環境変数として扱います。
完成コードを動かす
StaticFinalDemo.javaとして保存します。
class SchoolMember {
static final String SCHOOL_NAME = "Ada High School";
static int count = 0;
final int id;
String name;
SchoolMember(String name) {
count++;
id = count;
this.name = name;
}
void showInfo() {
System.out.println(id + ": " + name + " / " + SCHOOL_NAME);
}
}
public class StaticFinalDemo {
public static void main(String[] args) {
SchoolMember first = new SchoolMember("Ada");
SchoolMember second = new SchoolMember("Ken");
first.showInfo();
second.showInfo();
System.out.println("人数: " + SchoolMember.count);
}
}
javac StaticFinalDemo.java
java StaticFinalDemo
countとSCHOOL_NAMEはオブジェクトではなくクラスに属するため、SchoolMember.countのようにクラス名から読むと意図が明確です。
実行中の値の変化は次のとおりです。
| タイミング | count | 新しいid |
|---|---|---|
| クラス読み込み後 | 0 | — |
| Adaを作成後 | 1 | 1 |
| Kenを作成後 | 2 | 2 |
idは各オブジェクトに保存され、countだけが次の作成でも共有されます。
成功確認
1: Ada / Ada High School
2: Ken / Ada High School
人数: 2
2つのオブジェクトが同じ学校名と人数を共有し、IDだけはそれぞれ異なることを確認します。
final classとfinal methodの補足
final classは継承できないクラス、finalメソッドは子クラスでオーバーライドできないメソッドです。再代入禁止とは対象が異なります。API設計上、継承による変更を許したくない時に使いますが、入門ではフィールドと定数を先に使えるようにしましょう。
よくあるつまずき
staticへ何でも置く
利用者ごとの名前や状態をstaticにすると、全オブジェクトで値が共有されます。オブジェクト固有の値は通常のフィールドにします。
finalなら参照先も完全に変わらないと思う
参照型のfinalは、別オブジェクトへの再代入を禁止します。参照しているオブジェクト自体の変更可否は、そのクラスの設計によります。
定数を小文字で書く
動作はしますが、通常のstaticフィールドと区別しにくくなります。static finalの定数は大文字で表します。
練習
図書館利用者を表すLibraryUserを作ってください。全員共通の図書館名をstatic final、作成人数をstatic、変更しない会員番号をfinalで表します。2人作って結果を表示してください。
次のステップ
Java入門シリーズの基礎はここまでです。Python・JavaScript・Javaの違いで各言語の得意分野を確認し、これまでのクラスとコレクションを使った小さな作品を作りましょう。