package-private(default) : 같은 패키지 내의 아무 클래스나 사용 가능
protected : 하위 클래스, 선언된 클래스와 같은 패키지에 있는 클래스에서도 사용 가능
public : 어디서도 사용 가능
규칙 14 public 클래스 안에는 public 필드를 두지 말고 접근자 메서드를 사용하라
규칙 15 변경 가능성을 최소화하라
변경 불가능(immutable) 클래스는 그 객체를 수정할 수 없는 클래스다. ex) String, 기본 자료형 클래스, BigInteger, BigDecimal
장점
불변 객체는 한가지 상태만 가지므로 단순
thread-safe하여 어떤 동기화도 필요 없다. (변경 불가능한 객체는 자유롭게 공유 가능하다.)
복사없이 그 내부도 공유가 가능하다. (String에서 같은 문자열을 참조하는 것)
다른 객체의 구성요소로도 훌륭하다.
단점
변경 불가능 객체의 유일한 단점은 값마다 별도의 객체를 만들어야 하므로 성능 문제가 발생할 수 있다.
대안
1.다단계 연산 가운데 자주 요구되는 것을 기본 연산(primitive)으로 제공
2.변경 가능한 package-private 동료 클래스를 사용, 같은 값을 다시 요구하면 캐시한 값(비-final 필드)을 반환하여 재계산 비용을 줄인다.(Flyweight)
불변 클래스를 만드는 방법
객체 상태를 변경하는 메서드(setter)를 제공하지 않는다.
계승할 수 없도록 한다. (final class, 생성자를 private 으로 정의하고 public static factory method를 제공)
모든 필드를 final로 선언한다.
모든 필드를 private로 선언한다.
변경 가능 컴포넌트에 대한 독점적 접근권을 보장한다.(참조를 획득할 수 없도록...)
변경 가능한 클래스로 만들 타당한 이유가 없다면, 반드시 변경 불가능한 클래스로 만들어야한다, 변경 불가능한 클래스로 만들 수 없다면, 변경 가능성을 최대한 제한하라, 특별한 이유가 없다면 모든 필드는 final로 선언하라.
규칙 16 계승하는 대신 구성하라
* 메서드 호출과 달리, 계승은 캡슐화(encapsulation) 원칙을 위반한다, 계승 메커니즘은 상위 클래스의 문제를 하위 클래스에 전파시킨다. 반면 구성 기법은 그런 약점을 감추는 새로운 API를 설계할 수 있도록 해 준다.
*
wrapper class, decorator pattern, 구성과 전달기법을 아울러 위임(delegation)이라고 부르기도 한다.
장점
안정적이며, 유연성이 아주 높다.(타입, 이미 사용 중인 객체에 일시적으로 원하는 기능을 넣는 데도 사용)
메모리 요구량도 실제로 큰 영향이 없는 것으로 판명
단점
거의 없지만, 역호출(callback) 프레임워크와 함께 사용하기에는 적합하지 않다.
클래스 B는 클래스 A와 "Is-A" 관계가 성립할 때만 A를 계승해야 한다. "아니다"라고 답했다면, B안에 A 객체를 참조하는 private 필드를 두고, B에는 더 작고 간단한 API를 구현해야 한다. A는 B의 핵심적 부분이 아니며, B의 구현 세부사항에 불과하다. 구성대신 계승을 사용하려 할때 반드시 물어야할 마지막 질문들은 이런것이다. 계승할 클래스의 API에 문제가 있는가? 그렇다면, 그 문제들이 계속 새 API의 일부가 되어도 상관없겠는가?
규칙 17 계승을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 계승을 금지하라
* 재정의 기능 메서드를 내부적을 어떻게 사용하는지(self-use) 문서로 반드시 남겨라
*
클래스 내부 동작에 개입할 수 있는 훅(hooks)을 신중하게 고른 protected 메서드 형태로 제공해야 한다.
계승을 위해 설계한 클래스를 테스트할 유일한 방법은 하위 클래스를 직접 만들어 보는 것이다.
계승을 허용하려면 반드시 따라야 할 제약사항
생성자는 직접적이건 간접적이건 재정의 가능 메서드를 호출해서는 안된다.
Cloneable이나 Serializable을 구현하기로 결정했다면 clone이나 readObject 메서드 안에서 직접적이건 간접적이건 재정의 가능한 메서드를 호출하지 않도록 주의해야 한다.(생성자와 비슷하게 동작하므로 비슷한 규칙을 따라야 한다)
계승에 맞도록 설계하고 문서화하지 않은 클래스에 대한 하위 클래스는 만들지 않아야한다.
하위 클래스 생성을 금지하는 방법
클래스를 final로 선언.
모든 생성자를 private나 package-private로 선언하고 생성자 대신 public 정적 팩터리 메서드를 추가한다.
규칙 18 추상 클래스 대신 인터페이스를 사용하라
* 인터페이스는 믹스인(mixin)을 정의하는 데 이상적이다. (추가로 구현할 수 있는 자료형으로, 어떤 선택적 기능을 제공한다는 사실을 선언하기 위해 쓰인다.)
*
Java 1.8부터 default 메서드를 통해 인터페이스에도 구현을 일부 포함 가능
추상클래스 vs 인터페이스 (다양한 구현을 허용하는 자료형을 추상 클래스로 정의하면 인터페이스보다는 추상 클래스가 발전시키기 쉽다, 인터페이스는 공개되고 널리 구현된 다음에는, 인터페이스 수정이 거의 불가능하다.)
인터페이스는 비 계층적인 자료형 프레임워크를 만들 수 있도록 한다.
인터페이스를 사용하면 wrapper class을 통해 안전하면서도 강력한 기능 개선이 가능하다.
추상 골격 구현(abstract skeletal implementation) 클래스를 중요 인터페이스마다 두면, 인터페이스의 장점과 추상 클래스의 장점을 결합할 수 있다.
규칙 13 클래스와 멤버의 접근 권한은 최소화하라
규칙 14 public 클래스 안에는 public 필드를 두지 말고 접근자 메서드를 사용하라
규칙 15 변경 가능성을 최소화하라
장점
불변 객체는 한가지 상태만 가지므로 단순
thread-safe하여 어떤 동기화도 필요 없다. (변경 불가능한 객체는 자유롭게 공유 가능하다.)
복사없이 그 내부도 공유가 가능하다. (String에서 같은 문자열을 참조하는 것)
다른 객체의 구성요소로도 훌륭하다.
단점
변경 불가능 객체의 유일한 단점은 값마다 별도의 객체를 만들어야 하므로 성능 문제가 발생할 수 있다.
대안 1.다단계 연산 가운데 자주 요구되는 것을 기본 연산(primitive)으로 제공 2.변경 가능한 package-private 동료 클래스를 사용, 같은 값을 다시 요구하면 캐시한 값(비-final 필드)을 반환하여 재계산 비용을 줄인다.(Flyweight)
변경 가능한 클래스로 만들 타당한 이유가 없다면, 반드시 변경 불가능한 클래스로 만들어야한다, 변경 불가능한 클래스로 만들 수 없다면, 변경 가능성을 최대한 제한하라, 특별한 이유가 없다면 모든 필드는 final로 선언하라.
규칙 16 계승하는 대신 구성하라
wrapper class, decorator pattern, 구성과 전달기법을 아울러 위임(delegation)이라고 부르기도 한다.
장점
안정적이며, 유연성이 아주 높다.(타입, 이미 사용 중인 객체에 일시적으로 원하는 기능을 넣는 데도 사용)
메모리 요구량도 실제로 큰 영향이 없는 것으로 판명
단점
거의 없지만, 역호출(callback) 프레임워크와 함께 사용하기에는 적합하지 않다.
클래스 B는 클래스 A와 "Is-A" 관계가 성립할 때만 A를 계승해야 한다. "아니다"라고 답했다면, B안에 A 객체를 참조하는 private 필드를 두고, B에는 더 작고 간단한 API를 구현해야 한다. A는 B의 핵심적 부분이 아니며, B의 구현 세부사항에 불과하다. 구성대신 계승을 사용하려 할때 반드시 물어야할 마지막 질문들은 이런것이다. 계승할 클래스의 API에 문제가 있는가? 그렇다면, 그 문제들이 계속 새 API의 일부가 되어도 상관없겠는가?
규칙 17 계승을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 계승을 금지하라
클래스 내부 동작에 개입할 수 있는 훅(hooks)을 신중하게 고른 protected 메서드 형태로 제공해야 한다.
계승을 위해 설계한 클래스를 테스트할 유일한 방법은 하위 클래스를 직접 만들어 보는 것이다.
계승을 허용하려면 반드시 따라야 할 제약사항
생성자는 직접적이건 간접적이건 재정의 가능 메서드를 호출해서는 안된다.
Cloneable이나 Serializable을 구현하기로 결정했다면 clone이나 readObject 메서드 안에서 직접적이건 간접적이건 재정의 가능한 메서드를 호출하지 않도록 주의해야 한다.(생성자와 비슷하게 동작하므로 비슷한 규칙을 따라야 한다)
계승에 맞도록 설계하고 문서화하지 않은 클래스에 대한 하위 클래스는 만들지 않아야한다.
하위 클래스 생성을 금지하는 방법
클래스를 final로 선언.
모든 생성자를 private나 package-private로 선언하고 생성자 대신 public 정적 팩터리 메서드를 추가한다.
규칙 18 추상 클래스 대신 인터페이스를 사용하라
Java 1.8부터 default 메서드를 통해 인터페이스에도 구현을 일부 포함 가능
추상클래스 vs 인터페이스 (다양한 구현을 허용하는 자료형을 추상 클래스로 정의하면 인터페이스보다는 추상 클래스가 발전시키기 쉽다, 인터페이스는 공개되고 널리 구현된 다음에는, 인터페이스 수정이 거의 불가능하다.)
인터페이스는 비 계층적인 자료형 프레임워크를 만들 수 있도록 한다.
인터페이스를 사용하면 wrapper class을 통해 안전하면서도 강력한 기능 개선이 가능하다.
추상 골격 구현(abstract skeletal implementation) 클래스를 중요 인터페이스마다 두면, 인터페이스의 장점과 추상 클래스의 장점을 결합할 수 있다.
ex) AbstractionCollection, AbstractSet, AbstractList, AbstractMap, SkeletalCollection, SkeletalSet, SkeletalList, SkeletalMap
규칙 19 인터페이스는 자료형을 정의할 때만 사용하라
규칙 20 태그 달린 클래스 대신 클래스 계층을 활용하라
규칙 21 전략을 표현하고 싶을 때는 함수 객체를 사용하라
규칙 22 멤버 클래스는 가능하면 static으로 선언하라