Book/Effective Java

[교재 EffectiveJava] 아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라

shbada 2021. 10. 25. 13:01
728x90
반응형

톱레벨 클래스

소스 파일 하나에 톱레벨 클래스를 여러개 선언하더라도 자바 컴파일러는 불평하지 않는다. 하지만 아무런 득이 없고 심각한 위험을 감수해야한다. 

 

어느 소스파일을 먼저 컴파일하느냐에 따라 결과가 달라질 수 있는 위험이 있는데, 이를 예제를 통해 알아보자.

 

Utensil.java
package com.java.effective.item25;

class Utensil {
    static final String NAME = "pan";
}

class Dessert {
    static final String NAME = "cake";
}

 

Main.java
package com.java.effective.item25;

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

 

추가로 우연히 똑같은 두 클래스를 담은 Dessert.java 파일을 생성해보자.

 

Dessert.java
package com.java.effective.item25;

class Utensil {
    static final String NAME = "pot";
}

class Dessert {
    static final String NAME = "pie";
}

 

 

▶ 컴파일 에러 발생

javac Main.java Dessert.java // 컴파일 오류!

컴파일 에러가 발생한다. 클래스가 중복 정의되었다고 알려준다.

 

1) Main.java -> Utensil.java -> Dessert.java

System.out.println(Utensil.NAME + Dessert.NAME);

컴파일러는 가장 먼저 Main.java를 컴파일하고, 그 안에서 Utensil 참조가 먼저 나오므로, Utemsil.java 파일을 살펴 Utensil, Dessert 모두 찾아낼 것이다. 그런 다음 Dessert.java 를 처리하려고 할때 같은 클래스의 정의가 있음을 알게된다. 

 

2) "pancake" 결과

javac Main.java
javac Main.java Utensil.java

 

위 명령어로 컴파일하면 Dessert.java 파일을 작성하기 전처럼 정상적으로 "pancake" 결과를 출력한다. 

 

3) "potpie" 결과

javac Dessert.java Main.java

 

컴파일러에 어느 소스파일을 먼저 건네느냐에 따라 동작이 달라지게된다. 이는 큰 문제점이다.

 

 

해결책

아주 간단하다. 톱레켈 클래스(Utensil.java, Dessert.java)를 서로 다른 소스 파일로 분리하면 된다. 굳이 여러 톱레벨 클래스를 한 파일에 담고싶다면, 정적 멤버 클래스를 사용하는 방법을 고민해봐야한다. 

 

다른 클래스에 딸린 부차적인 클래스라면 정적 멤버 클래스로 만드는 쪽이 더 나을것이다. private 로 선언하면 접근 범위도 최소화하여 관리할 수 있다.

 

정적 멤버 클래스로 바꾼 Test.java
package com.java.effective.item25;

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

 

 

반응형