ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 클래스와 인터페이스 - 아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라
    Study/Effective Java 2022. 12. 27. 14:23

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

    톱레벨 클래스란?

    톱레벨 클래스란 함수, 클래스 또는 다른 무언가로 감싸지지 않은 모든 구문을 의미한다. 즉, 톱레벨 클래스란 중첩 클래스가 아닌 것을 말한다.

     

    다중 톱레벨 클래스의 문제점

    소스 파일 하나에 톱레벨 클래스를 여러 개 선언하더라도 자바 컴파일러는 불평하지 않는다. 하지만 아무런 득이 없을뿐더러 심각한 위험을 감수해야 하는 행위다. 이렇게 하면 한 클래스를 여러 가지로 정의할 수 있으며, 그중 어느 것을 사용할지는 어느 소스 파일을 먼저 컴파일하냐에 따라 달라지기 때문이다.

     

    위 문제점에 대한 구체적인 예를 들어 보겠다.

     

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

    위 Main,java 파일은 Main 클래스 하나를 담고 있고, Main 클래스는 다른 톱레벨 클래스 Utensil과 Dessert를 참조한다.

     

    // Utensil.java
    class Utensil {
        static final String NAME = "pan";
    }
    
    class Dessert {
        static final String NAME = "cake";
    }

    Utensil과 Dessert 클래스는 Utensil.java 파일에 위와 같이 정의되어 있다. Main 클래스를 실행하면 "pancake"를 실행한다.

     

    // Dessert.java
    class Utensil {
        static final String NAME = "pan";
    }
    
    class Dessert {
        static final String NAME = "cake";
    }

    이제 우연히, 똑같은 두 클래스를 담은 Dessert.java라는 파일을 만들었다고 해보겠다.

     

    문제 발생

    javac Main.java Dessert.java 명령으로 컴파일한다면 컴파일 오류가 나고 Utensil과 Dessert 클래스를 중복 정의했다고 알려줄 것이다. 그 이유는 컴파일러가 아래와 같은 순서로 작동했기 때문이다.

     

    1. Main.java 컴파일

    2. 그 안에 System.out.println(Utensil.NAME + Dessert.NAME); 구문을 만나기 때문에, Utensil.java를 컴파일

    3. 두 번째 인수로 넘어온 Dessert.java를 컴파일하려고 할 때 같은 클래스가 이미 정의되어 있다는 것을 알게 된다.

     

    한편, javac Main.javajavac Main.java Utensil.java 명령으로 컴파일하면 "pencake"를 출력한다. 또한, javac Dessert.java Main.java 명령으로 컴파일하면 "potpie"를 출력한다.

     

    이처럼, 컴파일러에 어느 소스 파일을 먼저 건네느냐에 따라 동작이 달라지므로 반드시 바로 잡아야 할 문제이다.

     

    해결책

    다행히 해결책은 아주 간단하다. 단순히 톱레벨 클래스들(Utensil, Dessert)을 서로 다른 소스 파일로 분리하면 그만이다.

     

    // Utensil.java
    public class Utensil {
        static final String NAME = "pan";
    }
    // Dessert.java
    public class Dessert {
        static final String NAME = "cake";
    }

    Utensil과 Dessert 클래스들을 서로 다른 소스 파일로 분리했다.

     

    굳이 여러 톱레벨 클래스를 한 파일에 담고 싶다면 정적 멤버 클래스를 사용하는 방법을 고민해볼 수 있다. 다른 클래스에 딸린 부차적인 클래스라면 정적 멤버 클래스로 만드는 쪽이 일반적으로 더 나을 것이다. 읽기 좋고, private로 선언하면 접근 범위도 최소한으로 관리할 수 있기 때문이다.

     

    public class Main {
        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";
        }
    }

    위 코드는 톱 레벨 클래스를 정적 멤버 클래스로 바꿔본 예이다.

     

    최종 정리

    소스 파일 하나에는 반드시 톱레벨 클래스를 하나만 담아야 한다. 이 규칙만 따른다면 컴파일러가 한 클래스에 대한 정의를 여러 개 만들어내는 일은 사라진다. 소스 파일을 어떤 순서로 컴파일하든 바이너리 파일이나 프로그램의 동작이 달라지는 일은 결코 일어나지 않을 것이다.


    출처

     이펙티브 자바 Effective Java 3/E. 조슈아 블로크 저자(글) · 개앞맵시(이복연) 번역

     

    728x90

    댓글

Designed by Tistory.