이펙티브 자바 4장에서는 클래스와 인터페이스 설계 요소에 대해 설명하고 있다.
견고하고 유연한 코드를 위해 자세하게 알아보도록 한다.
아이템15. 클래스와 멤버의 접근 권한을 최소화하라
모든 클래스와 멤버의 접근성을 가능한한 좁혀야 함 (정보 은닉 핵심)
외부에서 쓸 이유가 없다면 package-private
선언
- 권장 사항
public
클래스의 어떠한 필드도public
이면 안됨 (가변 필드를 가진 클래스는 스레드 안전하지 않음)public static final
배열 필드나 이를 반환하는 메서드 제공하면 안됨 (불변 리스트 or 복사본 반환 사용)
- 정보 은닉 장점
- 여러 컴포넌트 병렬 개발로 속도 향상
- 컴포넌트 교체 부담 감소, 빠른 디버깅으로 관리 비용 하락
- 다른 컴포넌트 영향없이 해당 컴포넌트만 최적화 가능
- 새로운 환경에서도 유용하게 쓰일 수 있어 재상용성 증가
- 개변 컴포넌트 동작 검증 가능하여 큰 시스템 제작 난이도 감소
- 접근 범위
private
: 멤버를 선언한 톱레벨 클래스에서만 접근package-private
: 소속된 패키지 안의 모든 클래스에서 접근 (명시하지 않으면 적)protected
:package-private
포함, 하위 클래스에서도 접근private
: 모든 곳에서 접근
아이템16. public
클래스에서는 public
필드가 아닌 접근자 메서드를 사용하라
패키지 바깥에서 접근할 수 있으면 접근자를 제공
package-private
, private
클래스라면 데이터 필드 노출해도 문제 없음
아이템17. 변경 가능성을 최소화하라
클래스는 필요한 경우가 아니라면 불변 불변으로 만들 수 없다면 변경 부분을 최소한으로
- 불변 클래스 규칙
- 객체 상태 변경 메서드 제공금지
- 클래스 확장 금지(
final
클래스 선언) - 모든 필드
final
선언 - 모든 필드
private
선언 - 내부 가변 컴포넌트에 접근 방지
- 불변 객체 장점
- 객체가 단순
- 스레드 안전, 동기화 필요 없음
- 불변 객체끼리 내부데이터 공유 가능
- 실패 원자성(메서드 예외 발생해도 객체는 유효) 제공
- 불변 객체는 값이 다르면 독립 객체 생성해야 한다는 단점 존재 (중간 단계에서 쓰고 버려지는 객체 발생)
- 다단계 연산들을 예측하여 기본 기능 제공
- 가변 동반 클래스를
public
으로 제공(ex.StringBuilder
)
아이템18. 상속보다는 컴포지션을 사용하라
이번 아이템은 인터페이스 상속과 무관
확장 대신 private
필드로 인스턴스 참조 사용(composition; 컴포지션)
기존 클래스의 대응 메서드를 호출하는 방식을 forwarding 방식이라고 하며, 새 클래스의 메서드는 forwarding method
상속은 정말 하위 타입인 상황에서만 사용
- 상속 단점
- 캡슐화가 깨짐
- 상위 클래스 메서드 다시 구현이 어렵고, 오류가 나거나 성능이 떨어질 수 있음
- 하위 클래스에서 콜렉션에 허용되지 않는 원소를 추가
- 하위 클래스 반환타입이 다르면 컴파일 불가능
- 불변식을 해칠 수 있음
- 컴포지션
- 인터페이스 활용으로 설계가 견고하고 유연
- 래퍼클래스 : 다른 인스턴스를 감싸고 있음
- 데코레이터 패턴 : 기능을 덧씌움
- 컴포지션과 전달의 조합은 넓은 의미로 위임
아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
내부적으로 어떻게 이용되고 있는지 문서화(Implementation Requirements)가 되어 있어야 함
Implementation Requirements 절은 @implSpec
태그를 붙이면 자바독 도구가 생성
- 효율적인 하위 클래스를 만들려면 내부동작 과정 중 훅을 선별하여
protected
메서드 형태로 공개 - 상속용 클래스의 생성자는 재정의 가능 메서드를 호출하면 안됨 (초기화 전에 메서드가 호출될 수 있음)
-
clone
,readObject
메서드는 생성자와 비슷한 역할, 재정의 가능 메서드를 호출하면 안됨 - 상속 금지
- 클래스를
final
선언 - 모든 생성자를
private
orpackage-private
선언, 정적 팩토리 메서드 이용
- 클래스를
아이템 20. 추상 클래스보다는 인터페이스를 우선하라
믹스인: 주된 타입 이외에 특정 선택정 행위를 제공 (ex. Comparable)
- 자바는 단일 상속만 지원되므로 추상 클래스 방식은 큰 제약
- 인터페이스는 기존 클래스에도 쉽게 구현 가능
- 인터페이스는 믹스인(mixin) 정의에 안성맞춤
- 인터페이스는 계층구조가 없는 타입 프레임워크(ex. SingerSongWriter) 만들 수 있음 (클래스로 만든다면 조합 폭발 발생)
- 래퍼 클래스를 이용하면 안전하게 기능 향상 가능 (추상 클래스에서 기능 추가하려면 상속뿐)
- 디폴트 메서드를 제공한다면
@implSpec
태그로 문서화 - 인터페이스와 추상 골격 구현(skeletal implementation)으로 두 가지 장점을 모두 취할 수도 있음 (ex.
AbstractCollection
,AbstractSet
…)- 관례상 골격 구현 클래스의 이름은
Abstract{인터페이스 명}
- 관례상 골격 구현 클래스의 이름은
- 골격 구현 대신 단순 구현(simple implementation)도 가능 (골격 구현과 다르게 추상클래스가 아님 ex.
SimpleEntry
)
아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라
디폴트 메서드로 새 메서드를 추가하는 일은 필요한 경우가 아니라면 피해야 함
새로운 인터페이스 생성을 추천
- 모든 상황에서 불변식을 해치지 않고 디폴트 메서드 작성은 쉽지 않음 (ex. 멀티 스레드 환경)
- 디폴트 메서드로 런타임 에러 발생 가능, 충돌 우려
- 인터페이스 설계에는 세심한 주의
아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라
- 인터페이스는 구현한 클래스의 타입 역할로만 사용해야 함
- 잘못된 예: 상수 인터페이스 (메서드 없이 상수만 선언, ex.
ObjectStreamConstants
)
- 잘못된 예: 상수 인터페이스 (메서드 없이 상수만 선언, ex.
- 상수 공개 방법
- 연관된 클래스나 인터페이스 자체에 추가 (ex.
Integer.MIN_VALUE
,Double.MAX_VALUE
) - 열거 타입
- 인스턴스화가 불가능한 유틸리티 클래스
- 연관된 클래스나 인터페이스 자체에 추가 (ex.
아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라
- 태그 달린 클래스 단점
- 장황 : 쓸 데 없이 비교하는 코드가 많음
- 오류 : 엉뚱한 필드로 초기화하면 런타임 중 에러
- 비효율 : 새로운 의미를 추가할 때마다 처리 코드 필요, 타입만으로 현재의 의미 파악이 어려움
- 클래스 계층 구조 변경 방법
- 계층구조의 루트가 될 추상 클래스 정의
- 태그에 따라 동작이 달라지는 메서드들을 추상 메서드로 선언
- 동작이 일정한 메서드들과 공통 데이터 필드를 루트 클래스에 추가
- 루트 클래스를 확장한 구체 클래스를 의미별로 정의
아이템 24. 멤버 클래스는 되도록 static
으로 만들라
정적 멤버 클래스
다른 클래스 안에 선언되고 바깥 클래스의private
멤버에 접근 가능. 이외에는 일반 클래스와 동일
중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스이어야 함 (비정적 멤버 클래스를 생성하려면 바깥 인스턴스가 필요) 중첩 클래스로는 정적 멤버 클래스, 멤버 클래스, 익명 클래스, 지역 클래스
- 비정적 멤버 클래스
- 인스턴스 안에 만들어져 메모리 공간 차지
- 생성 시간이 더 걸림
- 어댑터를 정의할 때 자주 사용 (ex.
ArrayList.Itr
) - 가비지 컬렉터가 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수 발생 가능
- 정적 멤버 클래스
- 바깥 클래스가 표현하는 객체의 한 부분을 나타낼 때 사용
- 익명 클래스
- 바깥 클래스의 멤버가 아님
- 어디서든 쓰이는 시점에 선언과 동시에 인스턴스 생성
- 비정적인 문맥에서만 바깥 클래스의 인스턴스 참조
- 정적 멤버 선언 불가
instanceof
또는 클래스 이름이 필요한 작업 불가- 여러 인터페이스 구현 및 구현과 동시에 상속이 불가
- 짧지 않으면 가독성 하락
- 정적 팩토리 메소드 구현이나 람다로 자주 사용
- 지역 클래스
- 어디서든 선언 가능, 유효 범위가 지역변수와 동일
- 반복해서 사용 가능
- 정적 멤버 선언 불가
- 짧지 않으면 가독성 하락
아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라
컴파일 순서에 따라 어느 것을 사용할지 달라질 수 있는 위험
→ 굳이 톱레벨 클래스를 한 파일에 담고싶다면 정적 멤버 클래스 사용