생성자 대신 정적 팩터리 메서드를 고려하라 클래스는 생성자와 별도로 정적 팩터리 메서드를 제공할 수 있다. 이는 그 클래스의 인스턴스를 반환하는 단순한 정적 메서드다. 장점 이름을 가질 수 있다. 생성자에 넘기는 매개변수와 생성자 자체만으로는 반환될 객체의 특성을 제대로 설명하지 못한다. 반면 정적 팩터리는 이름만 잘 지으면 반환될 객체의 특성...
생성자에 매개변수가 많다면 빌더를 고려하라 점층적 생성자 패턴을 사용할 수 있지만, 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어려워진다. 그리고 타입이 같은 매개변수가 연달아 늘어서 있으면 찾기 어려운 버그로 이어질 수 있다. 클라이언트가 실수로 매개변수의 순서를 바꿔도 컴파일러는 알 수 없고, 결국 런타임에 엉뚱한 동작을 하게 된...
private 생성자나 열거 타입으로 싱글턴임을 보증하라 싱글턴이라 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다. 싱글턴의 예로는 함수와 같은 무상태 객체나 설계상 유일해야 하는 시스템 컴포넌트를 들 수 있다. 그런데 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어렵다. 싱글턴을 만드는 방법 public static 멤버가 ...
자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다. 대신 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용하는 것이 좋다. 이를 활용한 변형으로는 생성자에 자원 팩터리를 넘겨주는 방식이 있다. (팩터리란 호출할 때마다 특정 타입의...
불필요한 객체 생성을 피하라 똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 특히 불변 객체는 언제든 재사용할 수 있다. 그렇기에 생성자 대신 정적 팩터리 매서드를 제공하는 불변 클래스에서는 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다. 생성 비용이 아주 비싼 객체도 더러 있다. 이런 '...
다 쓴 객체 참조를 해제하라 // 자바 GC 공부한 이후에 다시 공부하기 스택 보통의 스택 코드이다. 위의 코드에서 스택이 커졌다 줄어들었을 때 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수하지 않는다. 왜냐하면 스택이 그 객체들의 다 쓴 참조를 여전히 가지고 있기 때문이다. 그렇기에 메모리 누수가 일어날 수 있다고 한다. 그렇다면 메모리 누...
try-finally 보다는 try-with-resources를 사용하라 자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection 등이 좋은 예이다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다. 전통적...
equals는 일반 규약을 지켜 재정의하라 equals를 재정의하지 않는 경우 다음 열거한 상황 중 하나에 해당한다면 equals를 재정의하지 않는 것이 최선이다. 각 인스턴스가 본질적으로 고유하다. 값을 표현하는 게 아니라 동작하는 개체를 표현하는 클래스가 여기 해당한다. ex) Thread 인스턴스의 논리적 동치성을 검사할 일이 없다. 상...
equals를 재정의하려거든 hashCode도 재정의하라 equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다. 다음은 Object 명세에서 발췌한 규약이다. equals 비교에 사용되는 정보가 변경되지 않는다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 ...
toString을 항상 재정의하라 toString의 일반 규약에 따르면 '간결하면서 사람이 읽기 쉬운 형태의 유익한 정보'를 반환해야 한다. 또한 toString의 규약은 "모든 하위 클래스에서 이 메서드를 재정의하라"고 한다. 실전에서는 toString은 그 객체가 가진 주요 정보를 모두 반환하는 게 좋다. 그리고 toString을 구현한 때면 ...
clone 재정의는 주의해서 진행하라 clone 메서드가 선언된 곳은 Cloneable이 아닌 Object이고, 그마저도 protected로 선언되어 있다. 그래서 Cloneable을 구현하는 것만으로는 외부 객체에서 clone 메서드를 호출할 수 없다. 리플랙션을 사용하면 가능하지만, 100% 성공하는 것도 아니다. 해당 객체가 접근이 허용된 c...
Comparable을 구현할지 고려하라 우선 compareTo는 Object의 메서드가 아니다. compareTo는 단순 동치성 비교에 더해 순서까지 비교할 수 있으며, 제네릭하다. Comparable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서가 있음을 뜻한다. 모든 객체에 대해 전역 동치관계를 부여하는 equals 메서드와는 달리...
클래스와 멤버의 접근을 최소화하라 잘 설계된 컴포넌트는 클래스 내부 데이터와 내부 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐다. 잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다. 오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않는다. 그렇기에 모든 클래스와 멤버...
public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 철저한 객체 지향 프로그래머는 아래와 같은 클래스를 싫어해서 필드를 모두 private로 바꾸고 접근자(getter)를 추가한다. 위와 같이 패키지 바깥에서 접근할 수 있는 클래스라면 접근자를 제공함으로써 클래스 내부 표현 방식을 언제든 바꿀 수 있는 유연성을 얻을 수 있다. ...
변경 가능성을 최소화하라 불변 클래스란 간단히 말해 그 인스턴스의 내부 값을 수정할 수 없는 클래스다. 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다. 클래스를 불변으로 만들기 위해서는 아래 규칙을 따르면 된다. 객체의 상태를 변경하는 메서드를 제공하지 않는다. 클래스를 확장할 수 없도록 한다. 클래스를 ...
상속보다는 컴포지션을 사용하라 상속은 코드를 재사용하는 강력한 수단이지만, 항상 최선은 아니다. 상위 클래스와 하위 클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서라면 상속도 안전한 방법이다. 하지만 일반적인 구체 클래스를 경계를 넘어, 즉 다른 패키지의 구체 클래스를 상속하는 것은 위험하다. 메서드 호출과 달리 상속은 캡슐화를 깨뜨린다. ...
상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 메서드를 재정의하면 어떤 일이 일어나는지를 정확히 정리하여 문서로 남겨야 한다. 달리 말하면, 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다. 호출되는 메서드가 재정의 가능 메서드라면 그 사실을 호출하는 메서드의 API 설명에 적시해야 ...
추상 클래스보다는 인터페이스를 우선하라 추상 클래스와 인터페이스의 가장 큰 차이는 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다는 점이다. 인터페이스가 선언한 메서드르르 모두 정의하고 그 일반 규약을 잘 지킨 클래스는 다른 어떤 클래스를 상속했든 같은 타입으로 취급된다. 인터페이스는 믹스인 정의에 안성...
인터페이스는 타입을 정의하는 용도로만 사용하라 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지 클라이언트에 이야기해 주는 것이다. 인터페이스는 오직 이용도로만 사용해야 한다. 이 지침에 맞지 않는 예로 소위 상수 인터페이스가 있다. 상수 인터페이스 안티패턴은 인스턴스를 잘못 사용한 예이다. 클래스가 어떤 상수 인...
태그 달린 클래스보다는 클래스 계층구조를 활용하라 태그 달린 클래스에는 단점이 한가득하다. 우선 열거 타입 선언, 태그 필드, switch 문 등 쓸데없는 코드가 많다. 다른 의미를 위한 코드도 언제나 함께하니 메모리도 많이 사용한다. 필드를 final로 선언하려면 해당 의미에 쓰이지 않는 필드들까지 생성자에서 초기화해야 한다. 그리고 컴파일러가...
멤버 클래스는 되도록 static으로 만들라 중첩 클래스란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다. 중첩 클래스의 종류는 정적 멤버 클래스, (비정적) 멤버 클래스, 익명 클래스, 지역 클래스 이렇게 네 가지다. 이중 첫 번째를 제...
로 타입은 사용하지 말라 로 타입이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다. 로 타입은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데, 제네릭이 도래하기 전 코드와 호환되도록 하기 위한 궁여지책이라 할 수 있다. 오류는 가능한 발생한 즉시, 이상적으로는 컴파일할 때 발견하는 것이 좋다. 하지만 로 타입을...
배열보다는 리스트를 사용하라 배열과 제네릭 타입에는 중요한 차이가 2가지 있다. 첫 번째, 배열은 공변이다. 예를 들면 Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 된다. 반면, 제네릭은 불공변이다. 즉, 서로 다른 타입 Type1과 Type2가 있을 때, List은 List의 하위 타입도 아니고 상위...
이왕이면 제네릭 타입으로 만들라 E와 같은 실체화 불가 타입으로는 배열을 만들 수 없다. 배열을 사용하는 코드는 제네릭으로 만들려 할 때는 이 문제가 항상 발목을 잡을 것이다. 적절한 해결책은 두 가지다. 첫 번째는 제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법이다. Object 배열을 생성한 다음 제네릭 배열로 형병환하는 방법이다. ...
이왕이면 제네릭 메서드로 만들라 매개변수와 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다. (타입 매개변수들을 선언하는) 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다. 때때로 불변 객체를 여러 타입으로 활용할 수 있게 만들어야 할 때가 있는데 제네릭은 런타임에 타입 정보가 소거되므로 하나의 객체를 어떤 타입으로든 매개변수화할 ...
한정적 와일드카드를 사용해 API 유연성을 높이라 매개변수와 타입은 불공변이다. 즉, 서로 다른 타입 Type1과 Type2가 있을 때 List은 List의 하위 타입도 상위 타입도 아니다. List은 List의 하위 타입이 아니라는 뜻이다. List에는 어떤 객체든 넣을 수 있지만 List에는 문자열만 넣을 수 있다. List은 List가 하는...
제네릭과 가변인수를 함께 쓸 때는 신중하라 가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어진다. 그런데 내부로 감춰야 했을 이 배열을 클라이언트에게 노출하는 문제가 생겼다. 그 결과 varargs 매개변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고가 발생한다. 매개변수화 타입의 변수가 타입이 다른 ...
타입 안전 이종 컨테이너를 고려하라 제네릭은 Set, Map 등의 컬렉션과 ThreadLocal, AtomicReference 등의 단일원소 컨테이너에도 흔히 쓰인다. 이런 모든 쓰임에서 매개변수화되는 대상은 컨테이너 자신이다. 따라서 하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한된다. 하지만 더 유연한 수단이 필요한 때도 종종 있다....
int 상수 대신 열거 타입을 사용하라 열거 타입 자체는 클래스이며, 상수 하나당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 공개한다. 클라이언트가 인스턴스를 직접 생성하거나 확장할 수 없으니 열거 타입 선언으로 만들어진 인스턴스들은 딱 하나씩만 존재함이 보장된다. 다시 말해 열거 타입은 인스턴스 통제된다. 싱글턴은...
비트 필드 대신 EnumSet을 사용하라 우선 비트에 대해서 다시 한번 리마인드 해보자. 자바에서 역시 >와 같은 연산자를 사용하여 비트를 다룰 수 있다. 위의 THREE의 경우 ONE과 TWO를 OR 연산하여 나타낸 것을 볼 수 있다. 비트 필드를 사용하면 비트별 연산을 사용해 합집합과 교집합 같은 집합 연산을 효율적으로 수행할 수 있다. 하지...
ordinal 인덱싱 대신 EnumMap을 사용하라 Enum의 ordinal을 배열 인덱스로 사용하는 경우가 있다. 이때 문제는 배열은 제네릭과 호환되지 않아 비검사 형변환을 수행해야 하고 각 인덱스의 의미를 모르니 출력 결과에 직접 레이블을 달아야 한다. 가장 심각한 문제는 정확한 정숫값을 사용한다는 것을 직접 보증해야 한다는 점이다. 정수는 열...
확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라 타입 안전 열거 패턴은 확장할 수 있으나 열거 타입은 그럴 수 없다. 달리 말하면, 타입 안전 열거 패턴은 열거한 값들을 그대로 가져온 다음 값을 더 추가하여 다른 목적으로 쓸 수 있지만, 열거 타입은 그렇게 할 수 없다는 뜻이다. 사실 대부분 상황에서 열거 타입을 확장하는 건 좋지 않은 생...
명명 패턴보다 애너테이션을 사용하라 명명 패턴의 단점은 다음과 같다. 오타가 나면 안 된다. 올바른 프로그램 요소에서만 사용되리라 보증할 방법이 없다. 매개변수로 전달할 마땅한 방법이 없다. 애너테이션은 이 모든 문제를 해결해 주는 멋진 개념이다. 그렇기에 이번 글에서는 애너테이션의 동작 방식을 알아보자. @Retention과 @Target은 메...
정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 아무 메서드도 담고 있지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해 주는 인터페이스를 마커 인터페이스라 한다. ( ex) Serializable ) 이 마커 인터페이스는 두 가지 면에서 마커 애너테이션보다 낫다. 첫 번째, 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 ...
익명 클래스보다는 람다를 사용하라 자바에서 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스를 사용했다. 이런 인터페이스의 인스턴스를 함수 객체라고 하여, 특정 함수나 동작을 나타내는 데 썼다. JDK 1.1이 등장하면서 함수 객체를 만드는 주요 수단은 익명 클래스가 되었다. 전략 패턴처럼, 함수 객체를 사용하는 과거 객체 지향 디자인 ...
람다보다는 메서드 참조를 사용하라 매개변수의 수가 늘어난다면 메서드 참조로 제거할 수 있는 코드양도 늘어난다. 하지만 어떤 람다에서는 매개변수의 이름 자체가 프로그래머에게 좋은 가이드가 되기도 한다. 이런 람다는 길이는 더 길지만 메서드 참조보다 읽기 쉽고 유지보수도 쉬울 수 있다. 람다로 할 수 없는 일이라면 메서드 참조로도 할 수 없다. 그렇...
표준 함수형 인터페이스를 사용하라 자바 표준 라이브러리에 다양한 용도의 표준 함수형 인터페이스가 준비되어 있다. 필요한 용도에 맞는 게 있다면, 직접 구현하지 말고 표준 함수형 인터페이스를 활용하는 것이 좋다. 표준 함수형 인터페이스 대부분은 기본 타입만 지원한다. 그렇다고 기본 함수형 인터페이스에 박싱된 기본 타입을 넣어서 사용하지는 않아야 한다...
스트림은 주의해서 사용하라 스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻한다. 스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다. 스트림 파이프라인은 소스 스트림에서 시작해 종단 연산으로 끝나며, 그사이에 하나 이상의 중간 연산이 있을 수 있다. 각 중간 연산은 스트림을 어떠한 방식으로 변환한다. 스트림 파이프라인은 지...
스트림에서는 부작용 없는 함수를 사용하라 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수함수여야 한다. 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다. 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다. 이렇게 하...
반환 타입으로는 스트림보다는 컬렉션이 낫다 원소 시퀀스, 즉 일련의 원소를 반환하는 메서드는 수없이 많다. 자바 7까지는 이런 메서드이 반환 타입으로 Collection, Set, List 같은 컬렉션 인터펭스, 혹은 Iterable 이나 배열을 사용했다. 이 중 가장 적합한 타입은 컬렉션 인터페이스다. for-each 문에서만 쓰이거나 반환된 원...
스트림 병렬화는 주의해서 적용하라 환경이 아무리 좋더라도 데이터 소스가 Stream.iterate거나 중간 연산으로 limit를 쓰면 파이프라인 병렬화로는 성능 개선을 기대할 수 없다. 대체로 스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스거나 배열, int 범위, long 범위일 때 병렬...
매개변수가 유효한지 검사하라 메서드와 생성자 대부분의 입력 매개변수의 값이 특정 조건을 만족하기를 바란다. 그렇기에 이런 제약은 반드시 문서화해야 하며 메서드 몸체가 시작되기 전에 검사해야 한다. 이는 "오류는 가능한 한 빨리 잡아야 한다"는 일반 원칙의 사례이기도 하다. 오류를 발생한 즉시 잡지 못하면 해당 오류를 감지하기 어려워지고, 감지하더라...
적시에 방어적 복사본을 만들라 자바는 안전한 언어다. 하지만 아무리 자바라 해도 다른 클래스로부터의 침범을 아무런 노력 없이 다 막을 수 있는 건 아니다. 그러니 클라이언트가 불변식을 깨뜨리려 혈안이 되어 있다고 가정하고 방어적으로 프로그래밍하여야 한다. 어떤 객체든 그 객체의 허락 없이는 외부에서 내부를 수정하는 일은 불가능하다. 하지만 주의를...
메서드 시그니처를 신중히 설계하라 매개변수 목록은 짧게 유지하자. 4개 이하가 좋다고 책에는 나와있지만 개인적으로 2개 이하를 유지할 수 있도록 노력하는 것이 좋다고 생각한다. 이렇게 매개변수를 유지한다면 추후 자바에서 기본적으로 제공하는 함수형 인터페이스로의 리펙토링도 가능하기 때문이다. 그리고 같은 타입의 매개변수 여러 개가 연달아 나오는 경우...
다중정의는 신중히 사용하라 위의 코드의 결과는 "집합", "리스트", "그 외"일 것이라 생각하지만 실제로는 "그 외"만 세 번 연달아 출련된다. 그 이유는 classify에 중 어느 메서드를 호출할지가 컴파일타임에 정해지기 때문이다!!!! 컴파일타임에는 for 문 안의 c는 항상 Collection 타입이다. 런타임에는 타입이 매번 달라지지만, ...
가변인수는 신중히 사용하라 가변인수 메서드는 명시한 타입의 인수를 0개 이상 받을 수 있다. 그런데 받아야 하는 인수가 1개 이상이어야 하면 어떨까? 아마 아래와 같이 예외처리를 할 가능성이 크다. 하지만 이 방식에는 몇가지 문제가 있다. 만약에 인수를 0개만 넣어 호출하면 런타임에 실패한다는 것이다. 그럼 이를 해결할 다른 방법은 없을까? ...
null이 아닌, 빈 컬렉션이나 배열을 반환하라 컬렉션이나 배열 같은 컨테이너가 비었을 때 null을 반환하는 메서드를 사용할 때면 항시 방어 코드를 넣어주어야 한다. 클라이언트에서 방어 코드를 빼먹으면 오류가 발생할 수 있다. 한편 null을 반환하려면 반환하는 쪽에서도 이 상황을 특별히 취급해줘야 해서 코드가 더 복잡해진다. 때로는 빈 컨테이너...
옵셔널 반환은 신중히 하라 자바 8전에 메서드가 특정 조건에서 값을 반환할 수 없을 때 취할 수 있는 선택지는 다음과 같다. 예외를 던진다. null을 반환한다. 하지만 예외는 정말 예외적인 상황에서만 사용해야 하며 예외를 생성할 때 스택 추적 전체를 캡처하는 비용도 만만치 않다. null을 반환한다면 null을 처리하는 코드를 추가해야 한다. ...
공개된 API 요소에는 항상 문서화 주석을 작성하라 API를 쓸모 있게 하려면 잘 작성된 문서도 곁들여야 한다. 자바에서는 자바독이라는 유틸리티가 이 작업을 도와준다. 자바독은 소스코드 파일에서 문서화 주석이라는 특수한 형태로 기술된 설명을 추려 API 문서로 변환해 준다. 문서화 주석을 작성하는 규칙은 공식 언어 명세에 속하지는 않지만, 자바 프...
지역변수의 범위를 최소화하라 지역변수의 유효 범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고 오류 가능성은 적어진다. 지역변수의 범위를 줄이는 가장 강력한 기법은 '가장 처음 쓰일 때 선언하기'다. 또한 거의 모든 지역변수는 선언과 동시에 초기화해야 한
전통적인 for 문 보다는 for-each 문을 사용하라 for 문을 사용할 때 필요한 건 원소들뿐이지만 필요하지 않은 반복지와 인덱스 변수가 생긴다. 이는 모두 코드를 지저분하게 할 뿐이다. 이렇게 요소 종류가 늘어나면 오류가 생길 가능성이 커진다. 그리고 for 문을 사용하면 컬렉션이냐 배열이냐에 따라 코드 형태가 달라진다. 이러한 문제는 f...
박싱된 기본 타입보다는 기본 타입을 사용하라 오토박싱과 오토언박싱 덕분에 기본 타입과 참조 타입을 크게 구분하지 않고 사용할 수는 있지만, 그렇다고 차이가 사라지는 것은 아니다. 둘 사이에는 분명한 차이가 있다. 어떤 타입을 사용하는지는 상당히 중요하다. 주의해서 선택해야 한다. 기본 타입과 박싱된 타입의 주된 차이는 크게 세 가지다. 첫 번째...
다른 타입이 적절하다면 문자열 사용을 피하라 문자열을 쓰지 않아야 할 사례 문자열은 다른 값 타입을 대신 하기에 적합하지 않다. 많은 사람이 파일, 네트워크, 키보드 입력으로부터 데이터를 받을 대 주로 문자열을 사용한다. 입력받을 데이터가 진짜 문자열일 때만 그렇게 하는 게 좋다. 받은 데이터가 수치형이라면 int, float, BigIntege...
객체는 인터페이스를 사용해 참조하라 적합한 인터페이스만 있다면 매개변수뿐만 아니라 반환 값, 변수, 필드를 전부 인터페이스 타입으로 선언하는 것이 좋다. 객체의 실제 클래스를 사용해야 할 상황은 '오직' 생성자로 생성할 때뿐이다. 인터페이스를 타입으로 사용하는 습관을 길러두면 프로그램이 훨씬 더 유연해질 것이다. 나중에 구현 클래스를 교체하고자 하...
리플렉션보다는 인터페이스를 사용하라 리플렉션을 이용하면 컴파일 당시에 존재하지 않던 클래스도 이용할 수 있는데, 물론 단점이 있다. 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 리플렉션을 이용하면 코드가 지저분하고 장황해진다. 성능이 떨어진다. 리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 피하고 이점만 취할 수 있다. 컴파일타...
일반적으로 통용되는 명명 규칙을 따르라 패키지와 모듈 이름은 각 요소를 점(.)으로 구분하여 계층적으로 짓는다. 요소들은 모두 소문자 알파벳 혹은 숫자로 이뤄진다. 조직 바깥에서도 사용될 패키지라면 조직의 인터넷 도메인 이름을 역순으로 사용한다. 예외적으로 표준 라이브러리와 선택적 패키지들은 각각 java와 javax로 시작한다. 각 요소는 일반...
자바는 문제 상황을 알리는 타입으로 검사 예외, 런타임 예외, 에러, 이렇게 세 가지를 제공한다.호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용하라.검사 예외를 던지면 호출자가 그 예외를 catch로 잡아 처리하거나 더 바깥으로 전파하도록 강제하게 된다
필요 없는 검사 예외 사용은 피하라 검사 예외를 싫어하는 자바 프로그래머가 많지만 제대로 활용하면 API와 프로그램의 질을 높일 수 있다. 결과를 코드로 반환하거나 비검사 예외를 던지는 것과 달리, 검사 예외는 발생한 문제를 프로그래머가 처리하여 안정성을 높이게끔 해준다. 하지만, 검사 예외를 과하게 사용하면 오히려 쓰기 불편한 API가 된다. 어...
표준 예외를 사용하라 표준 예외를 재사용하면 얻는 게 많다. 그중 최고는 우리의 API가 다른 사람이 익히고 사용하기 쉬워진다는 것이다. 우리 API를 사용한 프로그램도 낯선 예외를 사용하지 않게 되어 읽기 쉽게 된다는 장점도 크다. 마지막으로, 예외 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다. Illegal...
추상화 수준에 맞는 예외를 던져라 수행하려는 일과 관련 없어 보이는 예외가 튀어나오면 당황스러울 것이다. 메서드가 저수준 예외를 처리하지 않고 바깥으로 전파해 버릴 때 종종 일어나는 일이다. 이는 프로그래머를 당황하게 하는 데 그치지 않고, 내부 구현 방식을 드러내어 윗 레벨 API를 오염시킨다. 다음 릴리스에서 구현 방식을 바꾸면 다른 예외가 튀...
메서드가 던지는 모든 예외를 문서화하라 검사 예외는 항상 따로따로 선언하고, 각 예외가 발생하는 상황을 자바 독의 @throws 태그를 사용하여 정확히 문서화하자. 하지만 예외는 있는데 main은 오직 JVM만이 호출하므로 Exception을 던지도록 선언해도 괜찮다. 자바 언어가 요구하는 것은 아니지만 비검사 예외도 검사 예외처럼 정성껏 문서화해 ...
예외의 상세 메시지에 실패 관련 정보를 담으라 예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적 정보를 자동으로 출력한다. 스택 추적은 예외 객체의 toString 메서드를 호출해 얻는 문자열로, 보통은 예외의 클래스 이름 뒤에 상세 메시지가 붙는 형태다. 이 정보가 실패 원인을 분석해야 하는 프로그래머 혹은 SRE가 얻을 수...
가능한 한 실패 원자적으로 만들라 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다. 이러한 특성을 실패 원자적이라 한다. 메서드를 실패 원자적으로 만드는 방법은 다양하다. 가장 간단한 방법은 불변 객체로 설계하는 것이다. 불변 객체는 태생적으로 실패 원자적이다. 가변 객체의 메서드를 실패 원자적으로 만드는 가장 흔...