25.1ファイルあたり1つのトップレベルクラスに制限せよ
1ファイルに複数のトップレベルクラスを定義することは可能だが、メリットは何もなく、リスクは存在する。
1ファイルに複数のトップレベルクラスを定義することの問題点
リスクとは、1つのクラス定義を複数してしまうというものである。複数定義されたうちのどれが使われるかは、ソースファイルがコンパイラーに渡された順に影響を受ける。
以下のような、2つのトップレベルクラスを参照するMainクラスを考える。
public class Main {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
}
Utensil.java という1つのファイルの中にUtensilクラスとDessertクラスを作る。
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}
この時Mainを実行すると、pancakeと表示が出る。
ここで、Dessert.java に以下のようなクラスを定義したとする。
class Utensil {
static final String NAME = "pot";
}
class Dessert {
static final String NAME = "pie";
}
ここで、javac Main.java Dessert.java というコマンドでコンパイルを実行した場合、コンパイルは失敗し、UtensilとDessertというクラスが重複していることを教えてくれる。
この時、コンパイラはMain.javaをまず見に行き、Utensilへの参照を見たときに、Utensil.javaを見に行き、UtensilとDessertクラスを見つける。コンパイラーがDessert.javaを見に行くときには、再びUtensilとDessertの定義を見つける。
もし、javac Main.java または、javac Main.java Utensil.javaとした場合には、Dessert.javaを書く前と同様の結果(pancake表示)になる。
しかし、javac Dessert.java Main.javaとした場合には、potpieと表示される。
このように、プログラムの挙動はコンパイルされる順番に影響されてしまうようになる。
解決法
解決策としては、シンプルに1つのソースファイルには1つのトップレベルクラスしか定義しないようにすることである。
もしくは、staticなメンバクラス(Item24)として定義してやる。
そのクラスが他の1つのクラスへの補助的な役割をするものであるなら、staticなメンバークラスにすることによって、可読性が向上し、無駄なアクセスを抑制することができる(staticメンバクラスをprivateにする)。
以下がその具体例である。
public class TestStaticMember {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
private static class Utensil {
static final String NAME = "pan";
}
private static class Dessert {
static final String NAME = "cake";
}
}