이펙티브 자바 Chapter8. 메서드

December 29, 2021 - 6 minute read -
book effective-java

이펙티브 자바 8장에서는 메서드 매개변수, 반환값, 시그니처 설계 등
메서드를 설계할 때 고려해야 할 점들에 대해 소개한다.


아이템 49. 매개변수가 유효한지 검사하라

매개변수들의 제약을 고려하고 명시적으로 검사하는 습관을 기르자

메서드가 몸체가 시작되기 전에 매개변수 제약(null 체크 같은)들을 검사해야 한다

  • 메서드가 중간에 모호한 예외를 던지며 실패 가능
  • 메서드가 수행되지만 잘못된 결과 반환 가능
  • 객체를 이상한 상태로 변경하여 알수 없는 시점에 관련 없는 오류 반환

publicprotected 메서드는 던지는 예외를 문서화 해야됨
→ 보통 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}
    • 상위 타입의 문서화 주석 일부를 상속