아이템15. 클래스와 멤버의 접근 권한을 최소화하라
2021-08-01 00:00:00 # Effective_Java

정보 은닉의 장점

  • 시스템 개발 속도를 높인다. (여러 컴포넌트를 병렬로 개발 가능하기 때문)
  • 시스템 관리 비용을 낮춘다. (각 컴포넌트를 더 빨리 파악해 디버깅 가능, 교체 부담 적음)
  • 성능 최적화에 도움을 준다. (다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화할 수 있기 때문)
  • 소프트웨어 재사용성을 높인다. (독자적으로 동작하는 컴포넌트는 여러 상황에서 유용함)
  • 큰 시스템을 제작하는 난이도를 낮춰준다. (시스템이 완벽하지 않아도 개별 컴포넌트의 동작 검증 가능)

모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다

  • public은 공개 API이기 때문에 하위 호환을 위해 영원히 관리해줘야만 한다.

  • 한 클래스에서만 사용하는 package-private 톱레벨 클래스나 인터페이스는 이를 사용하는 클래스 안에 private static으로 중첩시켜보자 [아이템 24]

  • public일 필요가 없는 클래스의 접근 수준을 package-private 톱레벨 클래스로 좁히자

4가지 접근 수준 점검~

  • private: 멤버를 선언한 톱레벨 클래스에서만 접근할 수 있다.
  • package-private: 멤버가 소속된 패키지 안의 모든 클래스에서 접근할 수 있다. 접근 제한자를 명시하지 않았을 때 적용되는 패키지 접근 수준이다. (단, 인터페이스의 멤버는 기본적으로 public이 적용된다).
  • protected: package-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다.
  • public: 모든 곳에서 접근할 수 있다.

중요해보이는 내용들

  • 멤버 접근성 좁히기: 클래스의 공개 API를 세심히 설계한 후, 그 외의 모든 멤버는 private으로 만들자. 그리고 오직 같은 패키지의 다른 클래스가 접근해야 하는 멤버에 한해 package-private으로 풀어주자.

  • protected의 멤버 수는 적을수록 좋다.

  • 상위 클래스의 메서드를 재정의할 때 그 접근 수준을 상위 클래스에서보다 좁게 설정할 수 없다는 제약으로 멤버 접근성을 좁히는 것에 방해가 될 때도 있음.

  • 코드를 테스트하려는 목적으로 접근 범위를 넓히는 것은 적당 수준까지 괜찮다.

    • public 클래스 내 private 멤버를 package-private까지는 허용가능이나 그 이상은 안 된다.
    • 테스트를 위해 클래스, 인터페이스, 멤버를 공개 API로 만들어서는 안 된다.
    • 테스트 코드를 테스트 대상과 같은 패키지에 두면 이럴 필요도 없다.
  • public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다 [아이템 16]

  • public 가변 필드를 갖는 클래스는 일반적으로 thread-safe 하지 않다.

  • 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다.

    • 제공하게 된다면 클라이언트에서 그 배열의 내용을 수정할 수 있게 된다. (당연하지;;)
    • 필요하다면 2가지 방법이 있지
    • 첫번째 방법: 앞 코드의 public 배열을 private으로 만들고 public 불변 리스트를 추가하기
    1
    2
    private static final Thing[] PRIVATE_VALUES = { ... };
    public static final List<Thing> VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
    • 두번째 방법: 배열을 private으로 만들고 그 복사본을 반환하는 public 메서드를 추가하는 방법 (방어적 복사)
    1
    2
    3
    4
    private static final Thing[] PRIVATE_VALUES = { ... };
    public static final Thing[] values() {
    return PRIVATE_VALUES.clone();
    }
  • 모듈 시스템을 활용해서 클래스를 외부에 공개하지 않으면서 같은 모듈을 이루는 패키지 사이에서 자유롭게 공유할 수 있게도 가능함. (protected나 public이라도 module-info.java와 같은 파일에 export하지 않으면)

    • 주의할 점: 한 모듈의 JAR 파일을 자신의 모듈 경로가 아닌 애플리케이션 classpath에 두면, 그 모듈 안의 모든 패키지는 마치 모듈이 없는 것처럼 행동한다. (모듈 공개 여부와 상관 없이 public 클래스가 선언한 모든 public 혹은 protected 멤버를 모듈 밖에서도 접근할 수 있게 된다.)
    • 대표적 예시가 JDK 그 자체
      • 자바 라이브러리에서 공개하지 않은 패키지들은 해당 모듈 밖에서는 절대로 접근할 수 없다.
    • 꼭 필요한 게 아니라면 자제하길

핵심 정리

  • 프로그램 요소의 접근성은 가능한 한 최소한으로 하라.
  • 꼭 필요한 것만 골라 최소한의 public API를 설계하자.
  • public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안 된다. 해당 필드가 참조하는 객체가 불변인지 확인하라.