4장. 클래스와 인터페이스 - GOAL
추상화의 기본 단위인 클래스와 인터페이스는 자바의 심장이다.
클래스와 인터페이스를 쓰기 편하고, 견고하며, 유연하게 만들어보자
아이템25. 톱레벨 클래스는 한 파일에 하나만 담으라
// 두 클래스가 Utensil.java 한 파일에 정의되었다. - 따라하지 말 것!
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}
// 두 클래스가 Dessert.java 에 정의되었다. - 따라하지 말 것
class Utensil {
static final String NAME = "pot";
}
class Dessert {
static final String NAME = "pie";
}
public class Main {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
}
위의 코드를 실행하면, 운 좋게 javac Main.java Dessert.java 명령으로 컴파일한다면 컴파일 오류가나고 Utensil과 Dessert 클래스를 중복 정의했다고 알려줄 것이다.
컴파일러는 가장 먼저 Main.java를 컴파일하고, 그 안에서 Utensil 참조를 만나면 Utensil.java 파일을 살펴 Utensil과 Dessert를 모두 찾아낼 것이다. 그런 다음 컴파일러가 두 번째 명령줄 인수로 넘어온 Dessert.java를 처리하려 할 때 같은 클래스의 정의가 이미 있음을 알게 된다.
해결책은 아주 간단하다. 단순히 톱레벨 클래스들(Utensil, Dessert)을 서로 다른 소스 파일로 분리하면 그만이다.
굳이 여러 톱레벨 클래스를 한 파일에 담고 싶다면 정적 멤버 클래스를 사용하는 방법을 고민해볼 수 있다. 다른 클래스에 딸린 부차적인 클래스라면 정적 멤버 클래스로 만드는 쪽이 일반적으로 더 나을 것이다. 읽기 좋고, private으로 선언하면 접근 범위도 최소로 관리할 수 있기 때문이다. 다음 코드는 앞의 예를 정적 멤버 클래스로 바꿔본 예다.
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";
}
}
결론
소스 파일 하나에는 반드시 톱레벨 클래스(혹은 톡레벨 인터페이스)를 하나만 담자.
이 규칙만 따른다면, 컴파일러가 한 클래스에 대한 정의를 여러 개 만들어 내는 일은 사라진다.
소스 파일을 어떤 순서로 컴파일하든 바이너리 파일이나 프로그램의 동작이 달라지는 일은 결코 일어나지 않을 것이다.
'Java > 이펙티브 자바' 카테고리의 다른 글
[이펙티브자바 - 아이템70] 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라 (0) | 2021.12.10 |
---|---|
[이펙티브자바 - 아이템69] 예외는 진짜 예외 상황에만 사용하라 (0) | 2021.12.10 |
[이펙티브자바 - 아이템24] 멤버 클래스는 되도록 static으로 만들라 (0) | 2021.10.10 |
[이펙티브자바 - 아이템23] 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (0) | 2021.10.10 |
[이펙티브자바 - 아이템22] 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2021.10.10 |