- 소스 파일 하나에 톱레벨 클래스를 여러 개 선언하더라도 자바 컴파일러는 문제 없이 컴파일 한다.
- 하지만 심각한 위험을 감수해야 하는 행위다.
- 이렇게 하면 한 클래스를 여러 가지로 정의할 수 잇으며, 그중 어느 것을 사용할지는 어느 소스 파일을 먼저 컴파일하느냐에 따라 달라지기 때문이다.
1
2
3
4
5
public class Main {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
}
1
2
3
4
5
6
7
8
// 코드 25-1 두 클래스가 한 파일(Utensil.java)에 정의되었다. - 따라 하지 말 것! (150쪽)
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}
- 위의 예제 코드를 보면 정상적으로 pancake이 출력된다.
- 하지만 아래 예시를 살펴보자.
1
2
3
4
5
6
7
8
// 코드 25-1 두 클래스가 한 파일(Utensil.java)에 정의되었다. - 따라 하지 말 것! (150쪽)
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}
- 운 좋게 javac Main.java Dessert.java 명령으로 컴팡리한다면 컴파일 오류가 나고 Utensil과 Dessert 클래스를 중복 정의했다고 알려줄 것이다.
- 컴파일러는 가장 먼저 Main.java를 컴파일 하고, 그안에서 (Dessert참조보다 먼저 나오는) Utensil 참조를 만나면 Utensil.java 파일을 살펴 Utensil과 Dessert를 모두 찾아낼 것이다.
- 그런 다음 컴파일러가 두 번째 명령줄 인수로 넘어온 Dessert.java를 처리하려 할 때 같은 클래스의 정의가 이미 있음을 알게 된다.
- 이처럼 컴파일러에 어느 소스 파일을 먼저 건네느냐에 따라 동작이 달라지므로 반드시 바로 잡아야할 문제이다.
해결책
- 해결책은 아주 간단하다. 톱레벨 클래스들(Utensil과 Dessert)를 서로 다른 소스 파일로 분리하면 된다.
- 굳이 여러 톱레벨 클래스를 한 파일에 담고 싶다면 정적 멤버 클래스(아이템 24)를 사용하는 방법을 고민해볼 수 있다.
- 이는 읽기 좋고, private으로 선언하면(아이템 15) 접근 범위도 최소로 관리할 수 있기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 코드 25-3 톱레벨 클래스들을 정적 멤버 클래스로 바꿔본 모습 (151-152쪽)
public class Test {
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";
}
}
Note: 소스 파일 하나에는 반드시!!! 톱레벨 클래스(혹은 톱레벨 인터페이스)를 하나만 담자. 컴파일러가 컴파일 순서에 따라 동작이 달라지는 일을 방지하자!