이펙티브 자바 - item 25. 톱레벨 클래스는 한 파일에 하나만 담으라

item 25. 톱레벨 클래스는 한 파일에 하나만 담으라

이펙티브 자바 - item 25. 톱레벨 클래스는 한 파일에 하나만 담으라

인터페이스 vs 추상 클래스

소스 파일 하나에 톱레벨 클래스를 여러개 선언하면 이득은 없고 심각한 위험이 발생할 수 있다. 한 클래스를 여러개로 정의할 수 있으며, 그 중 어느 소스파일을 먼저 컴파일하냐에 따라 달라지기 때문이다.

톱 클래스 중복 예시

아래와 같이다른 톱레벨 클래스 2개(Utensil과 Dessert)를 참조하는 Main 클래스가 있다고 해보자.

public class Main {
    public static void main(String[] args) {
    System.out.println(Utensil.NAME + Dessert.NAME);
    }
}

다음으로는 Utensil과 Dessert 클래스를 담은 Utensil.java, Dessert.java 파일이 있다고 가정해보자

// 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";
}

위와 같은 상황에서 main 함수를 실행시킨다면 어느 java 파일을 먼저 컴파일하냐에 따라 동작이 달라진다.

  1. 운좋게 javac Main.java Dessert.java 명령으로 컴파일하면 컴파일 오류가 나고 Utensil과 Dessert 클래스를 중복 정의했다고 알려줄 것이다. 컴파일러는 Main.java를 컴파일하고, 그 안에서 Utensil 참조를 만나면 Utencil.java 파일을 살펴 Utensil과 Desert를 모두 찾아 낼 것이다. 그런 다음 두번 째 명령줄 인수로 넘어온 Desset.java를 처리하려 할때 같은 클래스의 정의가 이미 있음을 알게된다.

  2. javac Main.javajavac Main.java Utensil.java 명령으로 컴파일 하면 pancake를 출력한다.

  3. javac Dessert.java Main.java 명령으로 컴파일하면 potpie를 출력한다.

이와 같이 컴파일 순서에 따라 동작이 달라진다.

해결방법

단순히 톱레벨 클래스들을 서로 다른 소스 파일로 분리하면 된다. 만약, 굳이 서로 다른 여러 톱레벨 클래스를 한 파일에 담고 싶다면 정적 멤버 클래스를 사용하자.

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";
    }

}

정리

소스 파일 하나에는 반드시 톱레벨 클래스(혹은 톱레벨 인터페이스)를 하나만 작성하자.