본문 바로가기

개발/이펙티브 자바

Effective Java ( 이펙티브 자바 ) - 아이템 25

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


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

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

파일명 - Utensil.java 가 정의되어있다.

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

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

이때 main을 실행하면 pancake를 출력한다.

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

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

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

운 좋게 javac Main.java Dessert.java 명령으로 컴파일한다면 컴파일 오류가 나고 중복 정의했다고 알려줄 것이다. 

한편, javac Main.java나 javac Main.java Utensil.java 명령으로 컴파일하면 Dessert.java 파일을 작성하기 전처럼 pancake를 출력한다. 그러나 javac Dessert.java Main.java로 컴파일하면 potpie를 출력한다. 

 

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

 

다행히 해결책은 아주 간단하다. 단순히 톱레벨 클래스들(Utensil, Dessert)을 서로 다른 소스 파일로 분리하면 그만이다. 굳이 한 파일에 담고 싶다면 정적 멤버 클래스를 사용하는 방법을 고민해볼 수 있다. 다른 클래스에 딸린 부차적인 클래스라면 일반적으로 더 나을 수 있다.

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";
   }
   
   priate static class Dessert {
      static final String NAME = "cake";
   }
}
소스 파일 하나에는 반드시 톱레벨 클래스(혹은 톱레벨 인터페이스)를 하나만 담자. 이 규칙만 따른다면 컴파일러가 한 클래스에 대한 정의를 여러 개 만들어 내는 일은 사라진다. 동작이 달라지는 일은 결코 일어나지 않을 것이다.