본문 바로가기

독서찰기(讀書札記)/이펙티브 자바

(73)
[아이템 24] 멤버 클래스는 되도록 static으로 만들라 중첩 클래스란? 정의 다른 클래스 안에 정의된 클래스 사용법 중첩 클래스는 자신을 감싼 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다. 종류 중첩 클래스의 종류에는 정적 멤버 클래스, (비정적)멤버 클래스, 익명 클래스, 지역 클래스가 있다. 첫번째를 제외한 나머지는 내부 클래스(inner class)에 해당한다. 정적 멤버 클래스 특징 다른 클래스 안에 선언 바깥 클래스의 private 멤버에 접근 가능 활용 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다. e.g.) 계산기가 지원하는 연산 종류를 정의하는 열거 타입 비정적 멤버 클래스 특징 비정적 멤버 클래스의 인스턴스와 바깥 인스턴스 사이의 관계는 멤버 클래스가 인스턴스화될 때 확립되며,..
[아이템 23] 태그 달린 클래스보다는 클래스 계층구조를 활용하라 [Why] class Figure { enum Shape { RECTANGLE, CIRCLE }; // 태그 필드 - 현재 모양을 나타낸다. final Shape shape; // 다음 필드들은 모양이 사각형(RECTANGLE)일 때만 쓰인다. double length; double width; // 다음 필드는 모양이 원(CIRCLE)일 때만 쓰인다. double radius; // 원용 생성자 Figure(double radius) { shape = Shape.CIRCLE; this.radius = radius; } // 사각형용 생성자 Figure(double length, double width) { shape = Shape.RECTANGLE; this.length = length; this.wi..
[아이템 22] 인터페이스는 타입을 정의하는 용도로만 사용하라 [Why] 인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 즉, 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해주는 것이다. 인터페이스는 오직 이 용도로만 사용해야 한다. [How] 안티 패턴으로 상수 인터페이스라는 것이 있다. 상수 인터페이스란 메서드 없이, 상수를 뜻하는 static final 필드로만 가득 찬 인터페이스를 말한다. 클래스 내부에서 사용하는 상수는 외부 인터페이스가 아니라 내부 구현에 해당한다. 상수 인터페이스를 구현하는 것은 이 내부 구현을 클래스의 API로 노출하는 행위다. 클래스가 어떤 상수 인터페이스를 사용하든 사용자에게는 아무런 의미가 없으며 오히려 혼란을 준다. 클라이언트 코드가 내부 ..
[아이템 21] 인터페이스는 구현하는 쪽을 생각해 설계하라 [Why] 자바 8에 와서 기존 인터페이스에 메서드를 추가할 수 있도록 디폴트 메서드를 소개했다. 디폴트 메서드를 선언하면, 인터페이스 구현 후 디폴트 메서드를 재정의하지 않은 모든 클래스에서 디폴트 구현이 쓰이게 된다. 하지만 모든 기존 구현체들과 매끄럽게 연동되리라는 보장은 없다. e.g.) 자바 8에서는 핵심 컬렉션 인터페이스들에 다수의 디폴트 메서드가 추가되었다. 주로 람다를 활용하기 위해서다. 디폴트 메서드는 컴파일에 성공하더라도 기존 구현체에 런타임 오류를 일으킬 수 있다. [How] 기존 인터페이스에 디폴트 메서드로 새 메서드를 추가하는 일은 꼭 필요한 경우가 아니면 피해야 한다. 디폴트 메서드는 인터페이스로부터 메서드를 제거하거나 기존 메서드의 시그니처를 수정하는 용도가 아님을 명심해야 한..
골격 구현(skeletal implementation) 클래스란? 골격 구현(skeletal implementation)은 인터페이스와 추상 클래스의 이점을 함께 사용할 수 있는 디자인이다. 상황 다양한 유형의 상품을 판매하는 자판기를 만들어보자. 코드 public interface Ivending { void start(); void chooseProduct(); void stop(); void process(); } // 사탕 자판기 public class CandyVending implements Ivending { @Override public void start() { System.out.println("Start Vending machine"); } @Override public void chooseProduct() { System.out.println("P..
[아이템 20] 추상 클래스보다는 인터페이스를 우선하라 [Why] 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다. 자바는 단일 상속만 지원하니, 추상 클래스 방식은 새로운 타입을 정의하는 데 커다란 제약을 안게 되는 셈이다. 인터페이스가 선언한 메서드를 모두 정의하고 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급된다. 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해넣을 수 있다. 클래스 선언에 implements 구문만 추가하면 된다. 기존 클래스 위에 새로운 추상 클래스를 끼워넣기는 일반적으로 어렵다. 인터페이스는 믹스인(mixin) 정의에 안성맞춤이다. 인터페이스로는 계층구조가 없는 타입 프레임워크를 만들 수 있다. e.g.) Singer 인터페이스, Songwriter ..
[아이템 19] 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라. 상속을 고려한 설계와 문서화란? 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다. [Why] 상속을 염두에 두지 않고 설계했고, 상속할 때의 주의점도 문서화해놓지 않은 '외부' 클래스를 상속할 때는 위험하다. 아이템18 상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다. 상위 클래스의 생성자가 하위 클래스의 생성자보다 먼저 실행되므로 하위 클래스에서 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출된다. 이때 그 재정의한 메서드가 하위 클래스의 생성자에서 초기화하는 값에 의존한다면 의도대로 동작하지 않는다. public class Super { // 잘못된 예 - 생성자가 재정의 가능 메서드를 호출한다. ..
[아이템 18] 상속보다는 컴포지션을 사용하라 ※ 여기서 말하는 '상속'은 클래스가 다른 클래스를 확장하는 구현 상속을 말한다. 클래스 인터페이스를 구현하거나 인터페이스가 다른 인터페이스를 확장하는 인터페이스 상속과는 무관하다. ※ 상위 클래스와 하위 클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서라면 상속도 안전한 방법이다. 확장할 목적으로 설계되었고 문서화도 잘 된 클래스아이템19도 마찬가지로 안전하다. 하지만, 일반적인 구체 클래스를 패키지 경계를 넘어, 즉 다른 패키지의 구체 클래스를 상속하는 일은 위험하다. [Why] 메서드 호출과 달리 상속은 캡슐화를 깨뜨린다. 상속은 반드시 하위 클래스가 상위 클래스의 '진짜' 하위 타입인 상황에서만 쓰여야 한다. 클래스 A를 상속하는 클래스 B를 작성하려 한다면 "B가 정말 A인가?"라고 자문해..