이펙티브 자바 8장에서는 메서드 매개변수, 반환값, 시그니처 설계 등
메서드를 설계할 때 고려해야 할 점들에 대해 소개한다.
아이템 49. 매개변수가 유효한지 검사하라
매개변수들의 제약을 고려하고 명시적으로 검사하는 습관을 기르자
메서드가 몸체가 시작되기 전에 매개변수 제약(null
체크 같은)들을 검사해야 한다
- 메서드가 중간에 모호한 예외를 던지며 실패 가능
- 메서드가 수행되지만 잘못된 결과 반환 가능
- 객체를 이상한 상태로 변경하여 알수 없는 시점에 관련 없는 오류 반환
public
과 protected
메서드는 던지는 예외를 문서화 해야됨
→ 보통 IllegalArgumentException
, IndexOutOfBoundsException
, NullPointerException
java7 에 추가된 Objects.requireNonNull
메서드,
java9 에 추가된 checkFromIndexSize
, checkFromToIndex
, checkIndex
메서드를 사용해도 좋다
유효성 검사 규칙 예외
- 유효성 검사 비용이 지나치게 높거나 실용적이지 않음
- 계산과정에서 암묵적으로 검사가 수행
- 하지만 암묵적인 유효 검사에 너무 의존하면 실패 원자성을 해칠 수 있음
아이템 50. 적시에 방어적 복사본을 만들라
클라이언트가 불변식을 깬다고 생각하고 방어적으로 프로그래밍하자
- 불변 객체 사용
- 가변 매개변수를 방어적으로 복사(defensive copy) 하여 사용
- 방어적으로 복사하고 복사본으로 유효성 검사
- 매개변수가 외부 클라이언트에게 확장될 수 있는 타입이라면
clone
사용 금지 (악의를 가진 하위 타입으로 복사될 수 있음)
- 접근자 메서드에서는 가변 필드의 방어적 복사본을 반환
- 이 경우에는
clone
사용 가능
- 이 경우에는
방어적 복사에는 성능 저하가 따를 수 있다.
클래스와 클라이언트가 상호 신뢰할 수 있다면 불변식이 깨지더라도 방어적 복사를 생략해도 된다.
대신, 매개변수나 반환값을 수정하면 안된다고 문서화하자
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // 수정되면 안됨
위처럼 Date
사용으로 인해 불변식이 깨질 수 있다.
Date
는 예전 api 이므로 불변인 Instant
, LocalDateTime
, ZonedDateTime
을 이용하자
아이템 51. 메서드 시그니처를 신중히 설계하라
메서드 이름을 신중히 짓자
- 항상 표준 명명 규칙을 따라야 함
- 같은 패키지에 속한 이름들과 일관되게 짓기
- 개발자 커뮤니티에서 널리 받아들여지는 이름으로 짓기
- 긴 이름 피하기
편의 메서드를 너무 많이 만들지 말자
- 메서드가 너무 많은 클래스와 인터페이스는 사용하고, 문서화하고, 테스트, 유지보수가 어려움
- 확신이 서지 않으면 만들지 말자
매개변수 목록은 짧게 유지
- 4개 이하 추천
- 같은 타입의 매개변수가 여러개 나오면 순서가 잘못되도 실행될 수 있어 문제가 된다
매개변수 목록을 줄이는 방법
- 매개변수 목록을 부분집합으로 받도록 여러 메서드로 쪼개기
- 매개변수 여러 개를 묶어주는 도우미 클래스 만들기
- 빌더 패턴을 메서드 호출에 응용
- 매개변수들의 객체를 정의하고
setter
를 호출해 필요한 값 설정
- 매개변수들의 객체를 정의하고
매개변수의 타입으로는 클래스보다는 인터페이스가 낫다
- 인터페이스를 직접 이용 (ex.
HashMap
보다Map
)
boolean
보다는 원소 2개짜리 열거 타입이 낫다
- 메서드 이름이
boolean
을 받아야 명확할 때는 예외 - 새로운 타입이 생긴다면 쉽게 추가할 수 있음
- 다른 의존성이 생긴다면 리팩터링이 가능
아이템 52. 다중정의(overloading)는 신중히 사용하라
- 어느 메서드가 호출할지는 컴파일 타임에 정해지기 때문에 예측하지 못한 결과가 나올 수 있음
- 재정의한 메서드는 동적으로, 다중정의한 메서드는 정적으로 선택되기 때문에 직관적이지 않다
- 다중정의로 혼동이 생기는 상황을 피하자
- 배개변수 수가 같은 다중정의는 만들지 말자
- 다중 정의 대신 메서드 명을 길게 지어라
- 명확히 구분될 수 있다면 괜찮음
- 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안된다.
- 근본적으로 다르지 않기 때문에 컴파일 에러가 발생할 수 있다.
아이템 53. 가변인수는 신중히 사용하라
- 가변인수 메서드는 명시한 타입의 인수를 0개 이상 받을 수 있다.
- 반드시 1개 이상이어야 한다면 배열 길이 검증보다는 매개변수를 2개 받도록 하자
(ex.static int min(int firstArg, int... remainingArgs) { ... }
) - 가변인수 메서드는 호출될때마다 배열을 새로 생성하고 초기화 한다. 성능에 민감한 상황이라면 인수 갯수마다 메서드를 다중정의하자
(ex.
foo(int a1)
,foo(int a1, int a2)
,foo(int a1, int a2, ...rest)
)
아이템 54. null
이 아닌, 빈 컬렉션이나 배열을 반환하라
null
을 반환하면 클라이언트에서는 방어코드가 필요해서 코드가 복잡해지고 빼먹으면 오류가 발생할 수 있다.- 매번 빈 컬렉션 할당으로 성능이 걱정된다면 똑같은 빈 불변 컬렉션을 반환하자 (배열도 마찬가지)
ex.Collections.emptyList
,Collections.emptySet
아이템 55. 옵셔널 반환은 신중히 하라
- 결과가 없을 수 있고, 클라이언트가 없는 경우를 특별하게 처리해야한다면
Optional<T>
반환 Optional
반환 하는 메서드에서 절대null
반환 금지Optional
은 검사 예외 취지와 비슷하여 사용자에게 명확히 값이 없음을 알려줌- 비검사 예외 또는
null
반환하면 사용자가 인지하기 어려움
- 비검사 예외 또는
- 컨테이너 타입은
Optional
으로 감싸면 안됨 (ex. 컬렉션, 배열, 스트림, 옵셔널)- 빈 컨테이너를 그대로 반환
- 박싱된 기본 타입 옵셔널은 반환하지 말자
OptionalInt
,OptionalLong
,OptionalDouble
Boolean
,Byte
,Character
,Short
,Float
은 예외
- 컬렉션의 키, 값, 원소로 사용하지 말자
- 값이 없다는 사실이 두가지가 되어 복잡성만 높아짐
메서드에서 특정 조건의 값을 반환할 수 없는 경우
- 예외를 던지기
- 진짜 예외적인 상황에서만 사용해야 함
- 스택 추적 전체를 캡처하게 되어 비용이 큼
null
반환- 별도의
null
처리 코드 필요 - 관련 없는 곳에서
NullPointerException
발생할 수 있음
- 별도의
Optional
반환 (java8 이후)- 기본 값 설정 가능
orElse
- 기본 값 설정 비용이 부담되면
orElseGet
- 원하는 예외 던지기 가능
orElseThrow
- 바로 값 꺼내기기 가능
get
filter
,map
,flatMap
,ifPresent
등 적절하게 사용 가능
- 기본 값 설정 가능
아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라
- 문서화의 첫번째 문장은 요약 설명, 요약 설명이 똑같은 멤버가 둘이상이면 안됨
- 마침표(.)로 요약 기준이 정해지므로 주의, 필요하다면
{@literal}
,{@summary}
사용
- 마침표(.)로 요약 기준이 정해지므로 주의, 필요하다면
- java9에서는 HTML 문서에 검색 기능 추가
- 클래스, 메서드, 필드 색인은 자동 추가, 용어를 추가하고 싶다면
{@index}
사용
- 클래스, 메서드, 필드 색인은 자동 추가, 용어를 추가하고 싶다면
- 메서드용 문서화 주석에는 규약을 명료하게 작성하자
- 메서드를 호출하기 위한 전제조건과 사후조건을 모두 나열하라
- 패키지 설명은
package-info.java
, 자바9부터 지원되는 모듈 시스템 설명은module-info.java
- 여러 클래스가 상호작용하는 복잡한 API라면 문서의 링크를 제공해도 좋음
문서화
- 올바로 문서화하려면 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에 문서화 주석이 있어야 한다.
- 시스템 상태에 변화를 가져오는 부작용도 문서화 (ex. 백그라운드 스레드를 시작)
- 제네릭 타입이나 제네릭 메서드에는 모든 타입 매개변수에 주석 추가 (
@param
사용) - 열거 타입 모든 상수들에도 주석을 달아야 함
- 애너테이션 타입의 모든 멤버들에도 주석을 달아야 함
- 클래스, 정적 메서드에는 스레드 안전수준을 반드시 설명에 포함
- 직렬화할 수 있는 클래스라명 직렬화 형태 작성
자바독 태그
@throws
- 비검사 예외를 선언하여 암시적으로 기술
- 모든 예외에 달아야 함 (검사, 비검사 모두)
- 보통 마침표 생략, 한글이고 종결어미로 끝난다면 써주는게 일관되어 보임
@param
- 조건에 영향을 받는 매개변수에 기술
- 보통 마침표 생략, 한글이고 종결어미로 끝난다면 써주는게 일관되어 보임
@return
- 반환 타입이
void
가 아니라면 달아야 함 - 메서드 설명과 같다면 생략 가능
- 보통 마침표 생략, 한글이고 종결어미로 끝난다면 써주는게 일관되어 보임
- 반환 타입이
{@code}
- java5에서 추가
- 태그로 감싼 내용을 코드용 폰트로 렌더링
- 태그로 감싼 내용에 포함된 HTML 요소나 자바독 태그 무시
- 코드가 여러 줄이라면
<pre>
태그로 감싸주기
@impleSpec
- java8에서 추가
- 자기사용 패턴에서 사용
- 해당 메서드와 하위 클래스 사이의 계약 설명
{@literal}
- java5에서 추가
- HTML 마크업이나 자바독 태그 무시
@code
와 다르게 코드 폰트로 렌더링 하지 않음
{@summary}
- java10에서 추가
- 요약 설명 전용 태그
- 메서드, 생성자는 동사구 / 클래스, 인터페이스, 필드는 명사절
{@index}
- 중요한 용어를 추가로 색인화
{@inheritDoc}
- 상위 타입의 문서화 주석 일부를 상속