이펙티브 자바-4장 클래스와 인터페이스

TL;DR

추상화의 기본 단위인 클래스와 인터페이스는 자바 언어의 심장과도 같다.
그래서 자바 언어에는 클래스와 인터페이스 설계에 사용하는 강력한 요소가 많이 있다.
이번 장에서는 이런 요소를 적절히 활용하여 클래스와 인터페이스를 쓰기 편하고, 견고하며, 유연하게 만드는 방법을 안내한다.


아이템 15. 클래스와 멤버의 접근 권한을 최소화하라

모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다.
클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다.
프로그램 요소의 접근성은 가능한 한 최소한으로 하라.
꼭 필요한 것만 골라 최소한의 public API를 설계하자.
그 외에는 클래스, 인터페이스, 멤버가 의도치 않게 API로 공개되는 일이 없도록 해야 한다.


아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

패키지 바깥에서 접근할 수 있는 클래스라면 접근자를 제공함으로써 클래스 내부 표현 방식을 언제든 바꿀 수 있는 유연성을 얻을 수 있다.
하지만, package-private 클래스 혹은 private 중첩 클래스라면 데이터 필드를 노출한다 해도 하등의 문제가 없다.


아이템 17. 변경 가능성을 최소화하라

클래스를 불변으로 만들기 위한 다섯 가지 규칙

  1. 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
  2. 클래스를 확장할 수 없도록 한다.
  3. 모든 필드를 final로 선언한다.
  4. 모든 필드를 private으로 선언한다.
  5. 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

불변 객체의 특징

불변 객체는 단순하다.
불변 객체는 생성된 시점의 상태를 파괴할 때까지 그대로 간직한다.
불변 객체는 근본적으로 스레드 안전하여 따로 동기화할 필요 없다.
불변 객체는 안심하고 공유할 수 있다.
객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다.
불변 객체는 그 자체로 실패 원자성을 제공한다.

불변 클래스의 단점

값이 다르면 반드시 독립된 객체로 만들어야 한다.
게터(getter)가 있다고 해서 무조건 세터(setter)를 만들지는 말자.
클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
불변 클래스는 장점이 많으며, 단점이라곤 특정 상황에서의 잠재적 성능 저하뿐이다.
모든 클래스를 불변으로 만들 수는 없다.
불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자.
다른 합당한 이유가 없다면 모든 필드는 private final이어야 한다.
생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.


아이템 18. 상속보다는 컴포지션을 사용하라

메서드 호출과 달리 상속은 캡슐화를 깨뜨린다.
상속의 취약점을 피하려면 상속 대신 컴포지션과 전달을 사용하자.
특히 래퍼 클래스로 구현할 적당한 인터페이스가 있다면 더욱 그렇다.
래퍼 클래스는 하위 클래스보다 견고하고 강력하다.


아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라

상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다.
클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅(hook)을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다.
상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.
상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
clonereadObject 모두 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
클래스를 상속용으로 설계하려면 엄청난 노력이 들고 그 클래스에 안기는 제약도 상당함을 알았다.
이 문제를 해결하는 가장 좋은 방법은 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것이다.
상속을 금지하려면 클래스를 final을 선언하거나 생성자 모두를 외부에서 접근할 수 없도록 만들면 된다.


아이템 20. 추상 클래스보다는 인터페이스를 우선하라

기존 클래스에도 손쉽게 새로운 인터페이스를 구현해넣을 수 있다.
인터페이스는 믹스인(mixin) 정의에 안성맞춤이다.
인터페이스로는 계층구조가 없는 타입 프레임워크를 만들 수 있다.
인터페이스는 기능을 향상시키는 안전하고 강력한 수단이 된다.
단순 구현(simple implementation)은 골격 구현의 작은 변종으로, AbstractMap.SimpleEntry가 좋은 예다.


아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라

생각할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기란 어려운 법이다.
디폴트 메서드는 (컴파일에 성공하더라도) 기존 구현체에 런타임 오류를 일으킬 수 있다.
핵심은 명백하다.
디폴트 메서드라는 도구가 생겼더라도 인터페이스를 설계할 때는 여전히 세심한 주의를 기울여야 한다.
인터페이스를 릴리스한 후라도 결함을 수정하는 게 가능한 경우도 있겠지만, 절대 그 가능성에 기대서는 안 된다.


아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라

상수 인터페이스 안티패턴은 인터페이스를 잘못 사용한 예다.
인터페이스는 타입을 정의하는 용도로만 사용해야 한다.
상수 공개용 수단으로 사용하지 말자.


아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라

태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적이다.
태그 달린 클래스는 클래스 계층 구조를 어설프게 흉내 낸 아류일 뿐이다.


아이템 24. 멤버 클래스는 되도록 static으로 만들라

멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만들자.
중첩 클래스에는 네 가지가 있으며, 각각의 쓰임이 다르다.
메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다.
멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로,
그렇지 않으면 정적으로 만들자.


아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라

소스 파일 하나에는 반드시 톱레벨 클래스 (혹은 톱레벨 인터페이스)를 하나만 담자.