클라이언트가 클래스의 인스턴스를 얻는 전통적인 수단은 public 생성자다. 그러나 하나 더 알아야하는 방식이 정적 팩터리 메소드(static factory method)이다. 정적 팩터리 메소드 방식의 장점과 단점을 알아보자.생성자에 넘기는 매개변수와 생성자 자체만으
정적 팩터리와 생성자에는 똑같은 제약이 하나 있다. 선택적 매개변수가 많을 때 적절히 대응하기 어렵다는 점이다. 생성자를 이용한 초기화, 자바빈즈 패턴(Setter), 빌더 패턴을 설명하면서 앞에서 얘기한 어렵다는 점에 대해 이해하도록 하겠다.위와 같이 Nutritio
싱글턴(singleton)이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 싱글턴을 만드는 방법 3가지를 설명하겠다.private생성자는 public static final필드인 Singleton1.instance를 초기화할 때 한번만 호출한다. publi
단순히 정적 메소드와 정적 필드만 담을 클래스를 만들고 싶을 때가 있다. java.util.Arrays나 java.util.Collections같이 말이다. final클래스와 관련한 메서드들을 모아놓을 때도 정적 메소드와 정적 필드만 담은 클래스를 만들 수 있다.정적
사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다.맞춤범 검사기는 사전에 의존한다. 맞춤법 검사기를 예로 설명하겠다.두 방식 모두 사전을 단 하나만 사용한다고 가정하는 점에서 좋은 코드가 아니다. 실전에서는 사전이 언어
똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 재사용은 빠르고 세련되다.위의 코드의 출력결과를 보면 같은 문자열을 가지는데 name1 객체와 name2 객체가 서로 다른 String 인스턴스인 것을 알 수 있다. 즉, 위와 같이
자바처럼 가비지 컬렉터를 갖춘 언어더라도 메모리 관리에 신경을 안 쓰면 안된다.위 코드에서 pop()의 코드를 보면 size만 조절할 뿐 더 이상 사용하지 않는 객체를 여전히 참조하고 있다. 객체 참조 하나를 살려두면 가비지 컬렉터는 그 객체뿐 아니라 그 객체가 참조하
자바는 finalizer와 cleaner라는 두 가지 객체 소멸자를 제공한다. 그 중 finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다. 오동작, 낮은 성능, 이식성 문제의 원이이 되기도 한다. 자바 9에서는 finalizer는 d
자바 라이브러리에서는 close()메서드를 호출해 직접 닫아줘야 하는 자원이 많다. 그러나 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다. 전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. 예외가 발
equals 메서드는 재정의하기 쉬워 보이지만 곳곳에 함정이 도사리고 있어서 끔찍한 결과를 초래할 수 있다. 함정을 피하는 가장 쉬운 길은 아예 재정의하지 않는 것이다.다음과 같은 상황 중 하나라도 해당하면 재정의하지 않는 것이 좋다.각 인스턴스가 본질적으로 고유하다.
equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다. 그렇지 않으면 hashCode 일반 규약을 어기게 되어 해당 클래스의 인스턴스를 HashMap이나 HashSet같은 컬렉션의 원소로 사용할 대 문제를 일으킬 것이다. 다음은 Object 명세의
Object의 기본 toString 메서드가 우리가 작성한 클래스에 적합한 문자열을 반환하는 경우는 거의 없다. toString의 일반 규약에 따르면 간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환해야 한다. 또한 toString의 규약에는 모든 하위 클래스에서
Cloneable은 복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이스지만, 아쉽게도 의도한 목적을 제대로 이루지 못했다. Cloneable의 문제점은 아래와 같다.clone 메서드가 선언된 곳이 Cloneable이 아닌 Object이고, protected이다.
Comparable 인터페이스의 compareTo메서드는 두 가지만 빼면 Object의 equals와 같다. 다른 두 가지는 다음과 같다.compareTo는 단순 동치성 비교에 더해 순서까지 비교할 수 있다.제너릭하다.Comparable을 구현했다는 것은 그 클래스의
아래와 같이 인스턴스 필드들을 모아놓은 일 외에는 아무 목적도 없는 클래스를 public으로 작성하면 안 된다.이런 클래스는 데이터 필드에 직접 접근할 수 있으니 캡슐화의 이점을 살리지 못한다. API를 수정하지 않고는 내부 표현을 바꿀 수 없고, 불변식을 보장할 수
잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 상관하지 않는다. 정보 은닉 혹은 캡슐화라고 하는 개념이다. 정보 은닉의 장점은 아래와 같다. 시스템 개발
불변 클래스는 그 인스턴스의 내부 값을 수정할 수 없는 클래스다. 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다. 불변 클래스는 가변 클래스보다 설계, 구현, 사용이 쉬우며 오류가 생길 여지도 적고 안전하다. 클래스를 불변으로 만
상속은 코드를 재사용하는 강력한 수단이지만, 항상 최선은 아니다. 잘못 사용하면 오류를 내기 쉽다. 상위 클래스와 하위 클래스가 같은 프로그래머가 통제하는 패키지 안에서라면 상속도 안전한 방법이다. 확장할 목적으로 설계되었고 문서화도 자 된 클래스도 마찬가지로 안전하다
상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다. 클래스의 API로 공개된 메서드에서 클래스 자신의 또 다른 메서드를 호출할 수도 있다. 그런데 마침 호출되는 메서드가 재정의 가능(public과 proetected
자바가 제공하는 다중 구현 메커니즘은 인터페이스와 추상 클래스 두 가지다. 자바 8부터 인터페이스도 디폴트 메서드를 제공할 수 있게 되어 이제는 두 메커니즘 모두 인스턴스 메서드를 구현 형태로 제공할 수 있다. 둘의 가장 큰 차이는 추상 클래스가 정의한 타입을 구현하
디폴트 메서드를 선언하면, 그 인터페이스를 구현한 후 디폴트 메서드를 재정의하지 않은 모든 클래스에서 디폴트 구현이 쓰이게 된다. 기존 인터페이스에 메서드를 추가할 수 있게 되었지만 모든 기존 구현체들과 매끄럽게 연동된다는 보장은 없다. 자바 7까지의 세상에서는 모든
인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 즉, 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해주는 것이다. 인터페이스는 오직 이 용도로만 사용해야 한다. 이 지침에 맞지 않
태그 달린 클래스란 한 클래스가 두 가지 이상의 의미를 표현하고, 그 중 현재 표현하고 있는 의므를 태그값으로 알려주는 클래스이다.태그 달린 클래스는 클래스 계층구조보다 훨씬 나쁘다. 우선 열거 타입 선언, 태그 필드, switch문 등 쓸데없는 코드가 많다. 여러 구
중첩 클래스란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 한다.중첩 클래스의 종류정적 멤버 클래스(비정적) 멤버 클래스익명 클래스지역 클래스정적 멤버 클래스를 제외한 나머지는 내부 클래스에 해당한다. 정적 멤버 클래스
소스 파일 하나에 톱레벨 클래스를 여러 개 선언하더라도 자바 컴파일러는 잘 작동한다. 하지만 아무런 득이 없을 뿐더러 심각한 위험을 감수해야 하는 행위다. 이렇게 하면 한 클래스를 여러 가지로 정의할 수 있으며, 그 중 어느 것을 사용할지는 어느 소스 파일이 먼저 컴파
클래스와 인터페이스 선언에 타입 매개변수가 쓰이면, 이를 제네릭 클래스 혹은 제네릭 인터페이스라 한다. 예를 들어 List 인터페이스는 원소의 타입을 나타내는 타입 매개변수 E를 받는다. 그래서 이 인터페이스의 완전한 이름은 List<E>이다. 제네릭 클래스와 제
제너릭을 사용하기 시작하면 수많은 컴파일러 경고를 보게 될 것이다. 비검사 형변환 경고, 비검사 메서드 호출 경고, 비검사 매개변수화 가변변수 타입 경고, 비검사 변환 경고 등이다.대부분의 비검사 경고는 쉽게 제거할 수 있다. 코드를 다음처럼 잘못 작성했다고 해보자.다
배열과 제너릭 타입에는 중요한 차이 두 가지가 있다.배열은 공변이지만 제너릭 타입은 불공변이다.배열은 실체화되어 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인한다. 제너릭은 타입 정보가 런타임에는 소거된다.Sub가 Super의 하위 타입이라면 배열 Sub\[
제네릭 타입을 새로 만드는 일은 조금 어렵지만, 그만한 값어치는 충분히 한다.아이템 7에서 다룬 스택 코드를 예로 들겠다.이 스택 클래스를 사용하는 클라이언트는 스택에서 꺼낸 객체를 형변환해야 하는데, 이때 런타임 오류가 날 수 있다. 이 스택 클래스를 제네릭 타입으로
클래스와 마찬가지로, 메서드도 제네릭으로 만들 수 있다. 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제너릭이다.제네릭 메서드의 작성법은 제네릭 타입 작성법과 비슷하다. 다음 예제 코드를 보면 된다.컴파일은 되지만 비검사 경고가 뜬다. 경고를 없애래면 이 메서드
아이템 28에서 이야기했듯 매개변수화 타입은 불공변이다. 즉, 서로 다른 타입 Type1과 Type2가 있을 때 List<Type1>은 List<Type2>의 하위 타입도 상위 타입도 아니다.하지만 때론 불공변 방식보다 유연한 방식이 필요하다. 아이템 29의
가변인수 메서드와 제네릭은 자바 5 때 함께 추가되었으니 서로 잘 어우러지리라 기대할 수도 있지만, 그렇지 않다. 가변 인수는 메서드에 넘기는 인수의 개수를 클라이언트에서 조절할 수 있게 해주는데, 구현 방식에 허점이 있다. 가변인수 메서드를 호출하면 가변인수를 담기
제네릭은 Set<E>, Map<K, V> 등의 컬렉션과 ThreadLocal<T>, AtomicReference<T> 등의 단일원소 컨테이너에도 흔히 쓰인다. 이런 모든 쓰임에서 매개변수화되는 대상은 컨테이너 자신이다. 따라서 하나의 컨테이너에서
열거 타입은 일정 개수의 상수 값을 정의한 다음, 그 외의 값을 허용하지 않는 타입이다. 사계절, 태양계의 행성, 카도게임의 카드 종류 등이 좋은 예다. 자바에서 열거 타입을 지원하기 전에는 다음 코드처럼 정수 상수를 한 묶음으로 선언해서 사용하곤 했다.정수 열거 패턴
대부분의 열거 타입 상수는 자연스럽게 하나의 정숫값에 대응된다. 그리고 모든 열거 타입은 해당 상수가 그 열거 타입에서 몇 번째 위치인지를 반환하는 ordinal이라는 메서드를 제공한다. 이런 이유로 열거 타입 상수와 연결된 정숫값이 필요하면 ordinal 메서드를 이
열거한 값들이 주로 집합으로 사용될 경우, 예전에는 상수에 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴을 사용해왔다.다음과 같은 식으로 비트별 OR을 사용해 여러 상수를 하나의 집합으로 모을 수 있으며, 이렇게 만들어진 집합을 비트 필드라 한다.비트 필드를 사
배열이나 리스트에서 원소를 꺼낼 때 ordinal 메서드(아이템 35)로 인덱스를 얻는 경우가 있다. 식물을 간단히 나타낸 다음 클래스를 예로 살펴본다.위 클래스를 사용하여 식물들의 생애주기별로 집합으로 묶어서 관리한다. 집합들은 배열 하나에 넣고 생애주기의 ordin
열거 타입은 거의 모든 상황에서 타입 안전 열겨 패턴보다 우수하다. 그러나 예외가 하나 있다. 타입 안전 열거 패턴은 확장할 수 있으나 열거 타입은 그럴 수 없다는 것이다. 타입 안전 열거 패턴은 열거한 값들을 그대로 가져온 다음 값을 추가하여 다른 목적으로 쓸 수 있
전통적으로 도구나 프레임워크가 특별히 다뤄야 할 프로그램 요소에는 딱 구분되는 명명 패텅능ㄹ 적용해왔다. 예를 들어 테스트 프레임워크인 JUnit은 버전 3까지 테스트 메서드 이름을 test로 시작하게 했다. 효과적인 방법이지만 단점도 컸다.오타가 나면 안 된다. 실수
자바가 기본으로 제공하는 애너테이션 중 보통의 프로그래머에게 가장 중요한 것은 @Override이다. @Override는 메서드 선언에만 달 수 있으며, 이 애너테이션이 달려다는 것은 상위 타입의 메서드를 재정의했음을 뜻한다. 이 애너테이션을 일관되게 사용하면 여러 가
아무 메서드도 담고 있지 않고, 단지 자신을 구현한 클래스가 특정 속성을 가짐을 표시해주는 인터페이스를 마커 인터페이스라 한다.Serializablie 인터페이스가 좋은 예다. Serializable은 자신을 구현한 클래스의 인스턴스는 ObjectOutputStream
자바에서 함수 객체를 만드는 주요 수단은 익명 클래스였다. 아래는 문자열을 길이순으로 정렬하는데, 정렬을 위한 비교 함수로 익명 클래스를 쓴 것이다.하지만 익명 클래스 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않았다.자바 8에서 함수형 인트페
람다가 익명 클래스보다 나은 점 중에서 가장 큰 특징은 간결함이다. 그런데 자바에는 함수 객체를 람다보다도 더 간결하게 만드는 방법이 있다. 그것은 바로 메서드 참조(method reference)다. 다음 코드는 임의의 키와 Integer 값의 매핑을 관리하는 프로그
자바가 람다를 지원하면서 API를 작성하는 모범 사례도 많이 바뀌었다. 템플릿 메서드 패턴의 매력이 크게 줄었다. 이를 대체하는 요즘 해법은 같은 효과의 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 것이다. 이것을 일반화해서 말하면 함수 객체를 매개변수로 받는 생
스트림 API는 다량의 데이터 처리 작업을 돕기 위해 자바 8에 추가됐다. 스트림 API의 핵심 개념은 두 가지다. 첫 번째인 스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻한다. 두 번째인 스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다.
스트림은 처음 봐서는 이해하기 어려울 수 있다. 스트림은 그저 또 하나의 API가 아닌, 함수형 프로그래밍에 기초한 패러다임이기 때문이다. 스트림이 제공하는 표현력, 속도, 병렬성을 얻으려면 API뿐만 아니라 패러다임까지 함께 받아들여야 한다.스트림 패러다임의 핵심은
원소 시퀀스, 즉 일련의 원소를 반환하는 방법은 여러 가지다. 기본은 컬렉션 인터페이스다. for-each문에서만 쓰어기나 반환된 원소 시퀀스가 일부 Collection 메서드를 구현할 수 없을 때는 Iterable 인터페이스를 썼다. 반환 원소들이 기본 타입이거나 성
동시성 프로그래밍 측면에서 자바는 항상 앞서갔다. 자바가 버전을 업데이트할수록 자바로 동시성 프로그램으 작성하기가 점점 쉬어지고는 있지만, 이를 올바르고 빠르게 작성하는 일은 여전히 어려운 일이다. 동시성 프로그래밍을 할 때는 안전성과 응답 가능 상태를 유지해야 한다.
메서드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하기를 바란다. 예를 들어 인덱스 값은 음수이면 안 되며, 객체 참조는 null이 아니어야 하는 식이다. 이런 제약은 반드시 문서화해야 하며 메서드 몸체가 시작되기 전에 검사해야 한다. 이는 오류는 가능한
자바는 안전한 언어다. 하지만 아무리 자바라 해도 다른 클래스로부터의 침범을 아무런 노력 없이 다 막을 수 있는 건 아니다. 그러니 클라이언트가 여러분의 불변식을 깨드리려 혈안이 되어 있다고 가정하고 방어적으로 프로그래밍해야 한다.외부 공격으로부터 인스턴스의 내부를 보
이번 아이템은 개별 아이템으로 두기 애매한 API 설계 요령들을 모아 놓았다.메서드 이름을 신중히 짓자. 항상 표준 명명 규칙을 따라야 한다. 이해할 수 있고, 같은 패키지에 속한 다른 이름들과 일관되게 짓는 게 최우선 목표다. 그 다음 목표는 개발자 커뮤니티에서 널리
재정의한 메서드는 동적으로 선택되고, 다중정의한 메서드는 정적으로 선택된다. 메서드를 재정의했다면 해당 객체의 런타임 타입이 어떤 메서드를 호출할지의 기준이 된다. 즉, 항상 '가장 하위에서 정의한' 재정의 메서드가 실행되는 것이다.한편, 다중정의된 메서드 사이에서는