해당 포스트는 "Effective Java" 책의 내용을 요약한 것이다.



- private : 선언된 최상위 레벨 클래스 내부에서만 접근 가능하다.

- package-private(default) : 같은 패키지 내의 아무 클래스에서 접근 가능하다. 접근 권한 수정자를 붙이지 않으면 해당 권한이 주어진다.

- protected : 같은 패키지 안에 있는 패키지와 선언된 클래스와 클래스의 하위 클래스에서 접근 가능하다.

- public : 어디서나 접근 가능하다.


※ 클래스와 멤버의 접근 권한은 최소화하라.

: 잘 설계된 모듈에서 가장 중요한 것은 모듈 내부의 데이터를 비롯한 구현 세부사항을 다른 모듈에 잘 감추냐의 여부이다. 즉, 정보 은닉과 캡슐화가 잘 되어있어야 한다. 그래야 모듈 사이의 의존성을 낮추어 개발/변경/최적화 등에서 좋은 결과를 얻을 수 있다. 이런 정보 은닉/캡슐화를 가능하게 해주는 핵심적인 요소가 접근권한이다. 


각 클래스와 멤버는 가능한 접근 불가능하도록 만들어야 한다. 즉, 최소화된 접근 권할을 설정해야 한다. 최상위 레벨 클래스와 인터페이스는 가능하면 package-private로 선언해야 한다. public으로 선언하면 전역적 객체가 되 API가 되 호완성 보장을 위해 해당 클래스를 계속 지원해야 한다. package-private로 선언하면 구현 세부 사항이 되 클라이언트의 코드와 관계없이 자유로운 수정을 할 수 있다. 만약 최상위 레벨 클래스와 인터페이스를 사용하는 클래스가 하나뿐이라면 해당 클래스 내부에 private 중첩 클래스를 만드는 것도 추천한다. 그러면  동일 패키지에서 접근이 불가능핟. 하지만 package-private의 접근 권한을 낮추는 것보다는 public의 접근 권한을 낮추는 게 중요하다. public은 api고 package-private는 구현 세부 사항이기 때문이다. proteceted 멤버 사용도 자제해야 한다. 접근 권한을 package-private에서 protected로 늘리면 protected 멤버는 해당 클래스의 공개 API가 되고 영원히 유지해야 하기 때문이다. 

이렇게 접근 권한을 최대한 줄여야 한다. 하지만 접근 권한을 줄일 수 없는 경우가 있는 데 그 것은 상위 레벨 클래스에서 재정의 하는 메서드들이다. 특히, 인터페이스를 상속할 경우 인터페이스 내 메서드들은 public 접근 권한을 가지기 때문에 재정의한 메서드들도 public으로 선언해야 한다. 


객체 필드(instance field), 비 final/final 참조 필드는 절대로 public으로 선언하면 안 된다. 다중 스레드에 안전하지 않다. 하지만 예외가 하나 있다. public static final 필드이다. 이 필드는 반드시 기본 자료형 값이나 변경 불가능 객체를 참조해야 한다. 또한 public static final 배열을 두거나 배열 필드를 반환하는 접근자를 정의하면 안 된다. public static final 배열 필드은 언제나 변경 가능하기 때문이다. 따라서 다음과 같은 코드를 사용하면 안 된다.

public static final Thing[] VALUES = { ... };

이에 대한 해결책 두 가지가 있는 데 다음과 같다.

private static final Thing[] PRIVATE_VALUES = { .... };
public static final List VALUES = 
           Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

public 이였던 배열을 private로 변경하고 변경이 불가능한 list를 만드는 것이다. 

private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
   return PRIVATE_VALUES.clone();
}

두 번째 방법은 배열을 private로 바꾸고 배열을 복사해서 반환하는 public 메서드를 추가하는 것이다.


접근 권한을 최소화 해라. 최소한의 public API를 설계해야 한다. 어떤 필드도 public으로 선언하지 말고 public staic final 필드의 경우 변경 불가능한 객체를 참조해야 한다.



※ public 클래스는 getter/setter 를 둬라

: public으로 선언된 클래스는 필드를 절대로 public으로 선언하면 안 된다. 다만 선언된 패키지 밖에서도 사용할 수 있도록 접근자 메서드(getter/setter)를 제공해야 한다. package-private 클래스나 private 중첩 클래스의 경우에는 필드를 공개하는 것도 나쁘지 않다. 패키지 외부 코드와 전혀 상관이 없기 때문이다. 


+ Recent posts