앞으로 나오는 package-private
은 접근제한자 default
와 동일하다고 본다.
잘 설계된 컴포넌트와 잘 설계하지 못한 컴포넌트는 내부 데이터를 외부에 얼마나 잘 숨겼는가만 봐도 알 수 있다.
잘설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨 구현과 API를 깔끔하게 분리한다.
오직 API를 통해서만 다른 컴포넌트와 소통하고 내부 동작의 방식에는 전혀 개의치 않는다.
가장 중요한 내용은 정보은닉 또는 캡슐화라고 우리가 흔히 부르는 내용이다.
:) 정보은닉 ( 캡슐화 )
- 시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문이다.
- 각 컴포넌트를 빠르게 파악하여 디버깅 가능, 다른 컴포넌트로 교체하는데 부담도 적다.
- 정보 은닉이 성능을 높여주는 않지만, 성능의 최적화에는 도움이 된다.
- 재사용성을 높여준다. 외부에 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면, 그 컴포넌트와 함께 개발되지 않은 환경에서도 유용하게 쓰일 가능성이 크다.
- 시스템 전체가 개발되지 않은 상태에서도 개별 컴퍼넌트의 동작을 검증 할 수 있다.
:) 모든 클래스와 멤버의 접근성을 가능한 좁혀라.
이 내용의 핵심은 클래스와 멤버의 접근제한자를 private등으로 낮은 접근 수준을 부여하는것이다.
우리가 가장 많이 사용하는 클래스에는 public 과 package-private 두가지를 가장 많이 사용한다.
Top Level Class나 Interface를 public 으로 선언하면 공개 API가 되지만 package-private 으로 선언하면 해당 패키지 않에서만 사용 가능하다.
패키지 외부에서 클래스를 사용할 일이 없다면 package-private로 선언하여 API가 아닌 내부에서 언제든지 수정할 수 있게 한다.
:) 한클래스에서만 사용하는 클래스나 인터페이스는 private static으로 중첩하라.
Item24에서 자세히 알아보겠지만, 탑 레벨로 두면 같은 패키지의 클래스들이 접근이 가능하지만 private static로 하면 outer class에서만 접근 할 수 있다.핵심은 public일 필요가 없는 클래스의 접근 수준을 package-private 톱 레벨 클래스의 접근 수준을 낮추는 것이다.
:) 멤버에 부여할 수 있는 접근제한자
멤버는 (필드, 메서드, 중첩 클래스, 중첩 인터페이스)를 의미
private
: 해당 멤버를 선언한 클래스에서만 접근이 가능하다.
package-private(default)
: 해당 멤버가 소속된 패키지 안의 모든클래스에서 접근할 수 있다. 접근제한자를 명시하지 않았을때 적용되는 default 접근 제한자이다. ( 단, interface는 default public이다 )
protected
: package-private의 범위를 포함하고 , 해당 멤버를 선언한 클래스를 상속받은 하위 클래스에서도 접근이 가능한 접근 제한자.
public
: 모든 곳에서 접근이 가능한 접근 제한자.
접근제한자는 위와 같이 4가지로 구현할 수 있다.
public 클래스에서는 멤버의 접근 수준을 좁히지 못하게 한느 제약이 하나 있다.
바로 상속받은 클래스가 구현 또는 선언한 메서드를 재 정의할때에는 그 접근제한자의 레벨보다 낮게 설정할 수 없다는 조건이다.
위의 조건은 상위 클래스의 인스턴스는 하위 클래스의 인스턴스를 대체해 사용할 수 있어야 한다는 (리스코프 치환 원칙)을 지키기 위해 필요하다.
위의 규칙을 어기게 되면 컴파일시 오류가 발생한다. 클래스가 인터페이스를 구현하는건 이 규칙의 특별한 예이고, 이때의 클래스는 인터페이스가 정의한 모든 메서드를 public으로 정의해야 한다.
:) public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 , 그필드에 값을 제한할 수 없기때문에 불변성을 보장할 수 없게 된다. 또한 필드가 수정될 때 다른 작업을 할 수 없게 되기 때문에, public 가변 필드를 갖는 클래스는 일반적으로 스레드로부터 안전하지 않다.
또한 필드가 final이면서 불변객체를 참조하더라도 문제는 발생할 수 있다. 이유는 final이기 때문에 다른 객체를 참조하도록 변경할 수 없지만, 이미 참조하고있는 참조된 객체 자체는 수정될수도 있다.
배열이 그 예 인듯 싶다.
이유는 해당 배열을 참조하는 참조값은 같지만 index값은 변경될 수 있기 때문이다, 따라서 클래스에서 public static final 배열필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공하면 안된다.
위와 같은 문제가 있지만 해결책도 있다. Effective Java에서는 2가지 방법을 제공한다.
- 코드의 public 배열을 private으로 만들고 public 불변 리스트를 추가하는 방법이다.
private static final Member MEMBER_VALUES = {...}; public static final List<Member> MEMBERS = Collections.unmodifiableList(Arrays.asList(MEMBER_VALUES));
- private 배열을 만들고 그 복사본을 반환하는 메서드를 추가하는 방법이다.
private static final Member MEMBER_VALUES = {...}; public static final Member[] members () { return MEMBER_VALUES.clone(); }
둘중에 어떤것이 더 좋을지는 어떤방식이 편할지 , 성능이 어떤게 더 좋을지 고민하여 정하면 될듯합니다.
Java9 에서는 모듈 시스템
이라는 개념이 도입되면서 2가지의 암ㅈ묵적 접근수준이 추가되었다.
모듈은 패키지의 묶음이다.
모듈은 자신이 속하는 패키지 중 공개할 것들을 module-info.java에 관례상 선언한다.
protected or public으로 선언되어있더라도 해당 패키지를 공개하지 않았다면 모듈 외부에서는 접근할 수 없다.
자바 9의 모듈에 대해서는 추후에 다뤄보도록 하겠습니다.
따라서 해당 Item15를 정리해보면 다음과 같다.
- 접근성은 가능한 최소한으로 하라.
- 꼭 필요한 정보들만 public API로 설계하라.
- class , interface , field가 의도치 않게 public 으로 공개되지 않도록 하라.
- public 클래스는 상수용 public static final 필드 외에는 public 필드도 가져서는 안된다.
- public static final이 참조하는 객체가 불변인지 확인하라.
'Java' 카테고리의 다른 글
[EffectiveJava] Item17 변경 가능성을 최소하라 (0) | 2021.01.06 |
---|---|
[EffectiveJava] Item16 public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2021.01.02 |
[EffectiveJava] Item14 Comparable을 구현할지 고려하라 (0) | 2020.12.27 |
[EffectiveJava] Item13 clone 재정의는 주의해서 진행하라. (0) | 2020.12.27 |
[EffectiveJava] Item12 toString을 항상 재정의하라 (0) | 2020.12.21 |
댓글