04. 클래스와 인터페이스
클래스와 인터페이스를 쓰기 편하고, 견고하며, 유연하게 만드는 방법 안내
Item 15. 클래스와 멤버의 접근 권한을 최소화하라#
잘 설계된 컴포넌트는 클래스의 내부 데이터와 구현 정보를 외부로 부터 잘 숨겨 놓았음 (정보은닉)
정보 은닉의 장점#
- 시스템 개발 속도를 높임
- 여러 컴포넌트 병렬로 개발할 수 있어서
 
 - 시스템 관리 비용을 낮춤
- 디버깅 및 컴포넌트 교체 비용 감소
 
 - 성능 최적화에 도움을 줌
- 다른 컴포넌트에 영향을 주지 않고 독립적 테스트가 가능해 짐
 
 - 재사용성 증가
 - 큰 시스템 제작 난이도 낮춰줌
- 시스템 전체가 완성되지 않은 상태로 개별 컴포넌트 동작 검증 가능
 
 
자바의 정보 은닉 지키는 실천 가이드#
- 접근 제한자 활용하여 모든 클래스와 멤버의 접근성을 가능한 좁혀야 함
 - 패키지 외부에 클래스가 쓸 일이 없다면 package-private으로 선언
 
package-private은 class 선언시 앞에 접근제어자를 적지 않아 패키지 내부까지만 사용할 수 있도록 클래스 범위를 제안하는 것. 같은 패키지에 포함되는 다른 클래스 까지만 접근이 가능함
protected는 가급적 줄이기- 상속 받은 모든 클래스에서 접근 가능해 의존성이 넓어짐
 
public클래스의 인스턴스 필드는 되도록public이 아니어야 함- 예외: 
public static fianl필드로 상수 공개- 이땐 반드시 기본 타입이나 불변 객체를 참조하여야 함
 public static final에서 가변 객체나 배열은 안됨- 굳이 두고 싶다면 다음과 같이 두가지로 가능
 
 
- 예외: 
 
private + public 불변 리스트 반환
private + public 방어적 복사 메서드 추가
- Java 9에서 제공하는 모듈단위 접근 수준이 2가지 있는데 아직은 쓰지않는게 좋다고 함
 
핵심 정리#
- 요소 접근성은 가능한 최소한
 - 클래스, 인터페이스, 멤버가 의도치 않게 API로 공개되는 일은 없도록
 - public 클래스는 상수용 public static final 외에는 public 필드를 가져선 안됨
 - public static final 필드는 불변인지 확인
 
Item 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라#
- public 클래스는 가변 필드를 직접 노출해선 안됨
 - 제목 그대로, 인스턴스 필드를 public으로 하기보단 getter/setter 를 통해 접근하게 하는 것을 말함
 - getter/setter 를 통해클래스 내부 표현 방식을 바꿀 수 있는 유연성이 생김
 - 예외: 종종 package-pirvate, private 중첩 클래스라면 데이터 필드를 노출해도 괜찮음 (외부로 번지지 않음)
 
Item 17. 변경 가능성을 최소화하라#
불변 클래스#
- 인스턴스의 내부값을 수정할 수 없는 클래스
 - String, 기본 타입 박싱 클래스, BigInteger, BigDecimal 등이 있음
 - 클래스를 불변으로 만드는 5가지 규칙
- 객체 상태를 변경하는 메서드를 제공하지 않는다
 - 클래스를 확장할 수 없도록 함 (클래스를 final 로 선언한다던지..)
- 모든 생성자를 private, package-private으로 만들고 정적 팩터리 메소드를 public으로 제공
 
 - 모든 필드를 final로 선언
 - 모든 필드를 private으로 선언
 - 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 함
- 생성자, 접근자, readObject 메서드 모두에서 방어적 복사를 수행
 
 
 - Complex 예시에서 
add가 아니라plus와 같은 전치사를 사용하였는데, 이는 내부 변화가 없다는 것을 의미하기 위해 구분하기 위한 용도 - 객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많음
- 불변성을 유지하기 쉬워짐
 
 - 값이 다르면 반드시 독립 객체를 만들어야함
 
불변 클래스 특징#
- 오류가 생길 여지가 적고, 가변 클래스보다 설계와 사용이 쉬움
 - 단순하다
 - thread-safety 를 보장하며 동기화 할 필요가 없음
- 따라서 안심하고 공유 가능함
 
 - 자주 사용하는 객체는 정적 팩터리로 인스턴스를 공유하여 사용 가능
- 방어적 복사도 필요 없음
 
 - 불변 객체끼리는 내부 데이터를 공유할 수 있음
 - 실패 원자성을 제공
- 예외를 발생시켜도 그 객체는 여전히 메서드 호출전과 같은 유효한 상태여야 한다는 성질
 
 
불변 클래스 BigInteger negate 예시를 파다보니 궁금한 사항#
BigInteger 내부 일부분
- 음 이렇게 카피하면 this.mag이 다른데에서 수정되었을때 같은 참조를 가지고 있는 BigInteger 객체에서 수정이 발생하지 않는가?
- 카피는 참조로 해도, 불변 객체라 연산 시 배열 카피가 일어남 (결과적으로 괜찮음)
 
 
test code
불변 클래스 주의사항#
- BigInteger, BigDecimal은 final 이 아니기 때문에 상속이 가능
- 상속 되었을 시 불변성은 보장 못하기 때문에 가변 인자라 취급하고, 타입 검사를 하고 방어 적 복사를 해야함
 
 
변경 가능 최소화 원리 핵심 정리#
- getter가 있다고 setter를 만들지 말자
 - 클래스는 꼭 필요한 경우 아니라면 불변이어야 함
 - 불변으로 만들 수 없는 클래스는 변경할 수 있는 부분을 최소한으로 줄이자
 - 다른 합당한 이유가 없다면 모든 필드는 private final 로 지정하자
 - 생성자는 불변식 설정이 모두 완료된, 초기화가 끝난 객체를 생성해야 함
- 확실한 이유가 없다면 생성자, 정적팩터리 외 다른 초기화 메서드 X
 
 
Item 18. 상속보단 컴포지션 사용#
- 다른 패키지의 구현 클래스를 상속하는 것은 위험
 - 구현 상속은 캡슐화를 깨트림
- 구현 클래스에 대한 상세 스펙을 이해해야 상속해서 재구현 가능
 
 
컴포지션#
- 기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 하자
 - 기존 클래스의 내부 구현 방식을 몰라도 됨
 - 새로운 메서드가 기존 클래스에 추가되더라도 영향을 받지 않음
- 물론 기존 메서드가 수정된다면 영향을 받을 수도 있음
 
 - 기존 클래스를 감싸는 측면에서 
Wrapper 클래스라고 부름 (InstrumentedSet 예시 참조)- 기존 클래스의 기능에서 다른 기능을 덧덴다는 의미에서 Decorator Pattern이라고 부름
 - 단점으론 콜백 프레임워크와 어울리지 않음
 
 - Wrapper 클래스는 
전달(Forwarding) 클래스를 통해 구현됨 (ForwardingSet 예시 참조)- 전달 클래스는 기존 인터페이스를 구현하고 해당 인터페이스를 컴포지션에서 가짐
 - Wrapper 클래스와 역할을 분리
 - 기존 다른 패키지와의 다리 역할을 함
 
 
만약 상속을 사용해야 한다면, 확장하려는 클래스에 결함이 없는지 반드시 다시 생각해보자. 상속하게 되면 기존 클래스의 결함/특징이 모두 따라오게 된다.
컴포지션 핵심 정리#
- 상속은 강력하지만 캡슐화를 해침
 - 상속의 취약점을 피할려면 컴포지션과 전달을 사용하자
 
Item 19. 상속을 고려해 설계하고 문서화하라, 그러지 않았다면 상속을 금지하라#
- 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야함
- protected, public 에 해당 (final 제외)
 - API 메서드 설명 중 Implementation Requirements 는 내부 동작 방식을 설명해주는 구간
- @implSpec 이 Java9 부터 본격적으로 사용 (매개 변수로 화렁화 할 수 있음)
 
 - 안전한 상속을 사용하려면 어쩔 수 없이 내부 구현 방식을 설명할 수 밖에 없음
 
 - 내부 동작 중 끼어둘 수 있는 hook을 잘 선별하여 protected 로 공개해야 할 수 있음
- 시험하는 방법은 직접 하위 클래스를 만들어볼 수 밖에 없음
 
 - 상속용 클래스의 생성자는 재정의 가능 메서드를 호출해선 안됨
 - clone과 readObject 모두 재정의 가능 메서드를 호출해선 안됨
 상속용 클래스로 설계되지 않은 클래스는 상속을 금지하는 것이 가장 중요- 클래스 final 선언
 - 모든 생성자를 private or package-private 으로 선언 + 정적 팩터리 메소드 제공
 
상속 핵심 정리#
- 상속용 클래스 설계는 쉽지 않음
 - 스스로 어떻게 사용하는지 문서화 되어 있어야함
 - 효율 좋은 하위클래스를 만들기 위해 protected로 제공해야 될 수도 있음
 - 확장할 명확한 이유가 없다면 상속 금지
 
Item 20. 추상 클래스보단 인터페이스를 우선하라#
- 자바는 단일 상속 계층을 따르므로 추상 클래스로는 한계가 있음
 - 고로 인터페이스 사용을 우선하는 것을 권장
 
인터페이스의 특징 및 사용처#
- 기존 클래스에도 손 쉽게 새로운 인터페이스를 구현할 수 있음
 - 믹스인에 안성맞춤
- mixin은 대상 타입의 주된 기능 외 선택적 기능을 혼합한다는 의미
 
 - 계층구조가 없는 타입 프레임워크를 만들 수 있음
 - 래퍼클래스 (item 18)와 함께 사용하면 인터페이스 기능을 향상 시키는 안정적인 수단이 됨
 - 인터페이스 + 추상 골격 구현 클래스로 인터페이스와 추상클래스의 장점을 모두 취하는 방법도 있음
- 단순히 골격 구현을 확장하는 것만으로 인터페이스 구현의 대부분 일이 완료될 수 있음
 Abstract*Interface이름을 주로 가짐- 추상 골격 클래스를 못 상속할 경우 인터페이스만 다시 구현하여 확장할 수 있음
 
 - 단순 구현은 골격 구현의 작은 변종
- 추상클래스로 선언하지 않은 인터페이스를 구현하는 가장 단순한 형태
 - 좋은 예로 
AbstractMap.SimpleEntry가 있음 
 
인터페이스 핵심 정리#
- 자바 다중 구현용 타입으로는 인터페이스가 가장 적합
 - 복잡한 인터페이스라면 골격 구현을 함께 제공하는 방법을 고려하자
- 골격 구현은 가능한 인터페이스의 디폴트 메서드로 제공
 
 
Item 21. 인터페이스는 구현하는 쪽을 생각해 설계하라#
- Java 8 전에는 기존 구현체를 깨트리지 않고 인터페이스에 메서드를 추가하는 방법읍 없었다
- Java 8 부터 인터페이스에 default method 가 등장했지만 위험이 완전 사라진건 아니다
 
 - 생각할 수 있는 모든 상황에서 불변식을 해치지 않는 default method는 작성하기 어렵다
 - 기존 구현체에 확장한 인터페이스의 default method를 그대로 사용하게 된다면 Runtime Error 를 발생시킬 수 있다
- 그러므로 기존 인터페이스에 default method로 새 메서드를 추가하는 일은 꼭 필요한 경우가 아니라면 피하는 것이 좋다
 
 - 새로운 인터페이스라면 릴리즈 전에 반드시 테스트를 거쳐야 한다
 
Item 22. 인터페이스는 타입을 정의하는 용도로만 사용하라#
- 인터페이스는 타입의 역할
 - 상수 인터페이스 안티패턴은 인터페이스를 잘못 사용한 예
 
상수 구현 선택지#
- 특정 클래스, 인터페이스와 강하게 결합되어 있으면 그 자체에 추가
 - 열거 타입
 - 인스턴스화 불가능한 유틸리티 클래스
 
Item 23. 태그 달린 클래스보다는 클래스 계층구조를 활용#
- 태그 값으로 현재를 표현하는 클래스가 있음
 
tag를 사용한 Figure 클래스
- 태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적
- 여러 구현이 한 클래스에 혼합되므로 가독성이 나쁨
 
 - 태그 구조를 클래스 계층구조로 리팩토링 하는게 코드 복잡성이 줄어듦
 
계층구조를 활용한 Figure 클래스
Item 24. 멤버 클래스는 되도록 static 으로 만들라#
중첩 클래스의 쓰임#
정적 멤버 클래스#
- 클래스 내에 정의되는 
static class - private으로 선언 시 해당 클래스를 정의한 바깥 클래스 에서만 접근 가능
 - public으로 사용할 경우도 있음
 - 멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙이자
 
(비정적) 멤버 클래스#
- 바깥 클래스의 인스턴스와 암묵적으로 연결됨 (정적 멤버 클래스와의 차이)
- 정규화된 this를 이용한 바깥 클래스의 메서드 호출 및 참조 가져오기 가능
 
 - 예시: Iterator를 자신이 만든 클래스에 맞게 재정의 해야할 때 커스텀 Iterator 클래스를 비정적 멤버 클래스로 만듦
 - 메모리 누수의 원인이 될 수 있음 (바깥 클래스가 생성될 때, 보이지 않는 참조가 멤버 인스턴스에 생길 수 있음)
 
익명 클래스#
- 선언한 시점에서만 인스턴스를 만들 수 있음
- 따라서 객체화를 한번만 할 때 사용
 
 - instanceof 검사, 클래스 이름이 필요한 작업은 수행 불가
 - 여러 인터페이스 불가
 - 인터페이스 구현 시 다른 클래스 상속 불가
 - 정적 팩터리 메서드를 구현할 때 주로 사용
 
지역 클래스#
- 지역 변수를 선언할 수 있는 곳 어디든 정의 가능
 - 사용할 일이 있을 지 모르겠음
 
중첩 클래스 정리#
- 메서드 밖에서도 사용하거나, 메서드안에 정의하기 길다면 
멤버 클래스- 멤버 클래스의 인스턴스 각각이 바깥 멤버 인스턴스의 접근이 필요하다면 
비정적 멤버 클래스- 그렇지않으면 
정적 멤버 클래스 
 - 그렇지않으면 
 
 - 멤버 클래스의 인스턴스 각각이 바깥 멤버 인스턴스의 접근이 필요하다면 
 - 중첩 클래스가 한 메서드 안에서만 쓰이고, 그 인스턴스를 생성하는 시점이 하나라면
- 해당 타입으로 쓰기에 적절한 클래스, 인터페이스가 이미 있다면 
익명 클래스 - 없다면 
지역 클래스 
 - 해당 타입으로 쓰기에 적절한 클래스, 인터페이스가 이미 있다면 
 
Item 25. 톱 레벨 클래스는 한 파일에 하나만 담으라#
- 혹시 하나의 파일에 클래스를 여러개 두고 싶은 유혹이 든다면,
- 그리고 최대한 참다가 이렇게 밖에 짤수밖에 없다면,
 - 한 파일의 탑 클래스 내에 정적 멤버 클래스로 구성할 수 있을 것이다