아이템 15. 클래스와 멤버의 접근 권한을 최소화하라
- 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
- 한 클래스에서만 사용하는 package-private 톱레벨 클래스나 인터페이스는 이를 사용하는 클래스 안에 private static으로 중첩시키는 편이 낫다.
- 클래스의 공개 API외의 모든 멤버는 private로 만들도록 한다.
권한 풀어주기를 자주 하게 된다면 컴포넌트를 더 분해해야 하는 것은 아닌지 다시 고민해보자.
- public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 그 필드에 담을 수 있는 값을 제한할 수 없다. (불변식 보장 불가, thread safe X)
- 꼭 필요한 상수라면 public static final로 공개하자.
이런 필드는 반드시 기본 타입 값이나 불변 객체를 참조해야 한다.
아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
- public 클래스는 절대 가변 필드를 직접 노출해서는 안된다.
API를 수정하지 않고는 내부 표현을 바꿀 수 없다.
불변식을 보장할 수 없다.
외부에서 필드에 접근할 때 부수적인 로직을 추가할 수 없다
- 캡슐화하면 클래스의 내부 표현 방식을 언제든 바꿀 수 있는 유연성을 제공할 수 있어서 좋다.
- package-private 클래스나 중첩 클래스에서는 노출해도 상관없다.
아이템 17. 변경 가능성을 최소화하라
불변 클래스는 인스턴스의 내부 값을 수정할 수 없는 클래스이다. 객체가 파괴되는 순간까지 값이 절대 달라지지 않는다.
- 장점
1. 스레드 안전하여 동기화가 필요 없다.
2. 1로 인해 안심하고 공유할 수 있다. → 방어적 복사, clone, 복사 생성자 필요없다.
3. 실패 원자성을 제공한다. (메서드에서 예외가 발생한 후에도 메서드 호출 전과 같은 상태)
- 단점
1. 값이 다르면 반드시 새로 만들어야 한다. → 성능 문제 있을수도
- 주의할 점
1. getter가 있다고 무조건 setter를 만들지는 말자
2. 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
3. 단순 값 객체는 항상 불변으로 만들자
아이템 18. 상속보다는 컴포지션을 사용하라
- 상속은 캡슐화를 깨뜨린다.
상위 클래스 구현에 따라 하위 클래스 동작에 이상이 생길 수 있음
상위 클래스의 변경이 있을 때마다 상속받은 클래스에 다른 영향이 없는지 매번 확인해줘야한다.
또한, 내부의 구현 방법을 모르는 상태로 상속을 하면 의도하지 않은 동작들이 발생할 수 있다.
- 상속 대신 컴포지션을 사용하자.
새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하도록
- 상속은 is-a 관계일때만 사용해야한다.
그래도 조심!
아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
- 상속은 캡슐화를 해친다. 안전하게 상속하려면 내부 구현을 설명해야한다.
- 상속용 클래스의 생성자는 재정의 가능 메서드를 호출하면 안된다.
하위 클래스의 생성자에서 상위 클래스의 생성자를 먼저 실행되어, 하위 클래스의 값들이 초기화되기 전에 재정의된 메서드가 호출되버림
- 상속은 힘들다. 그냥 상속을 금지하자.
- 상속을 금지하는 방법 두가지
1. final class로 선언한다.
2. private or default 생성자로 선언하고, 정적 팩토리 메서드 제공
아이템 20. 추상 클래스보다는 인터페이스를 우선하라
아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라
-기존 인터페이스에 디폴트 메서드로 새 메서드를 추가하는 것은 매우매우 조심해야 한다.
해당 인터페이스를 구현하고 있는 모든 클래스가 영향을 받는다.
꼭 필요한 경우가 아니라면 피해라.
- 나중에 추가하는 것은 위험하므로, 인터페이스를 설계할 때부터 세심하게 잘하자.
아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라
- 인터페이스를 구현한다는 것은 무슨 역할을 하는지 알려주는 것이다. 다른 용도로는 쓰이면 안된다.
- 상수 인터페이스는 몹쓸 짓이다.
- 상수를 공개하려면, 클래스 자체에 추가 or Enum or 유틸리티 클래스에 담자.
아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라
- 태그 달린 클래스는 단점이 한가득이다. 쓰지 말자.
쓸데없는 코드 많아짐, 가독성 나쁨, 메모리도 많이 차지함
→ 장황, 오류 취약, 비효율적
- 자바와 같은 객체 지향 언어에서는 서브타이핑 사용하자
유연성
컴파일타임 타입 검사 능력을 높여줌
아이템 24. 멤버 클래스는 되도록 static으로 만들라
중첩 클래스에는 정적 멤버 클래스, 비정적 멤버 클래스, 익명 클래스, 지역 클래스가 있다.
- 정적 멤버 클래스
바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 주로 쓰인다.
- 비정적 멤버 클래스
바깥 클래스의 인스턴스에 숨은 외부 참조를 갖게 된다. (바깥 인스턴스가 gc되지 않을 수 있으니 조심)
정규화된 this를 사용해 바깥 인스턴스의 참조를 가져올 수 있다.
어댑터를 정의할 때 주로 쓰인다. ex. HashMap.KeySet
→ 중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적으로 만들어야 한다. (불필요한데 비정적으로 만든다면 공간과 시간 낭비)
- 익명 클래스
사용 시점에 선언, 인스턴스 생성한다.
비정적 문맥에서 쓸 때만 바깥 인스턴스 참조 가능하다.
정적인 문맥에서 쓰일 때도, 상수 변수 외에 정적 멤버를 가질 수 없다.
가독성이 좋지 않다.
람다로 많이 대체되었다.
- 지역 클래스
지역변수스러운 클래스
비정적 문맥에서 쓸 때만 바깥 인스턴스 참조 가능하다.
이름이 있고, 반복 사용 가능하다.
아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라
- 소스파일 하나에는 반드시 톱레벨 클래스 하나만 담자
- 이런 짓을 하면, 한 클래스를 여러 가지로 정의할 수 있음. 뭘 사용할지는 컴파일 순서에 따라 달라짐 → 심각한 문제;
- 굳이 담고 싶다면 정적 멤버 클래스로 만들자 + private으로 만들면 너무 좋아