Effective Java 3/E - 8장 메서드 요약정리

8장 메서드

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

  • 메서드 몸체가 실행되기 전에 매개변수를 확인하면 잘못된 값이 넘어왔을 때 즉각적이고 깔끔한 방식으로 예외를 던질 수 있다.
  • 자바 7에 추가된 java.util.requireNonNull 메서드는 유연하고 사용하기도 편하니, 더 이상 null 검사를 수동으로 하지 않아도 된다.
  • 메서드나 생성자를 작성할 때 그 매개변수들에 어떤 제약이 있을지 생각해야 하고 그 제약들은 문서화하고 코드 시작 부분에서 명시적으로 검사해야 한다.

아이템 50 - 적시에 방어적 복사본을 만들라

  • 클라이언트가 여러분의 불변식을 깨뜨리려 혈안이 되어 있다고 가정하고 방어적으로 프로그래밍해야 한다.
  • 가변 필드를 생성자 매개변수로 받아 클래스 필드에 저장하는 경우 각각을 방어적으로 복사해야 한다.
  • Date는 낡은 API이니 새로운 코드를 작성할 때는 더 이상 사용하면 안된다.
  • 매개변수의 유효성 검사를 하기 전에 방어적 복사본을 만들고 이 복사본으로 유효성을 검사해야 한다.
  • 매개변수가 제3자에 의해 확장될 수 있는 타입이라면 방어적 복사본을 만들 때 clone을 사용해서는 안 된다.
  • 가변 필드를 반환하는 get 접근자 메서드에서는 가변 필드의 방어적 복사본을 반환하도록 해야 한다.

아이템 51 - 메서드 시그니처를 신중히 설계하라

메서드 이름을 신중히 짓자

이해할 수 있고 같은 패키지에 속한 다른 이름들과 일관되게 짓는 게 최우선 목표다.

편의 메서드를 너무 많이 만들지 말자

메서드가 너무 많으면 학습, 문서화, 테스트, 유지보수하기 어렵다.

매개변수 목록은 짧게 유지하자. 4개 이하가 좋다.

  • 과하게 긴 매개변수 목록을 짧게 줄여주는 기술 세 가지
    • 여러 메서드로 쪼갠다. (직교성을 높인다)
      • 예) List 인터페이스에서 지정된 부분 범위의 부분리스트에서 인덱스를 찾는 메서드를 별도로 제공하지 않고 subList, indexOf를 제공한 것
    • 매개변수 여러 개를 묶어주는 도우미 클래스를 만든다.
      • 예) class Point { int x; int y; }
    • 객체 생성에 사용한 빌더 패턴을 메서드 호출에 응용한다.
  • 매개변수 타입으로는 클래스보다는 인터페이스가 더 낫다.
  • boolean보다는 원소 2개짜리 열거 타입이 낫다.

아이템 52 - 다중정의는 신중히 사용하라

  • 다중정의(overloading)은 어느 메서드를 호출할지가 컴파일타임에 정해진다. 재정의(override)한 메서드는 동적으로 선택되고, 다중정의한 메서드는 정적으로 선택된다.
  • API 사용자가 매개변수를 넘기면서 어떤 다중정의 메서드가 호출될지를 모른다면 프로그램이 오동작하기 쉽다. 다중정의가 혼동을 일으키는 상황을 피해야 한다.
  • 안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말자.
    • 다중정의하는 대신 메서드 일므을 다르게 지어주는 방법이 있다.
  • 다중정의된 메서드들이 함수형 인터페이스를 인수로 받을 때 비록 서로 다른 함수형 인터페이스라도 인수 위치가 같으면 혼란이 생긴다. 따라서 메서드를 다중정의할 때 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안된다.
  • 프로그래밍 언어가 다중정의를 허용한다고 해서 다중정의를 꼭 활용하란 뜻은 아니다.

아이템 53 - 가변인수는 신중히 사용하라

<인수가 1개 이상이어야하는 가변인수 메서드 - 잘못 구현한 예!>

static int min(int... args) {
    if (args.length == 0) {
        throw new IllegalArgumentException("인수가 1개 이상 필요합니다.");
    }
    int min = args[0];
    for (int i = 1; i < args.length; i++) {
        if (args[i] < min)
            min = args[i];
    return min;
    }
}
  • 가장 심각한 문제는 인수를 0개만 넣어 호출하면 런타임에 실패한다는 점. (코드도 지저분함) args 유효성 검사를 명시적으로 해야한다.

<인수가 1개 이상이어야할 때 가변인수를 제대로 사용하는 방법>

static int min(int firstArg, int... remainingArgs) {
    int min = firstArgs;
    for (int arg : remainingArgs)
        if (arg < min)
            min = arg;
    return min;
}

성능에 민감한 경우

  • 가변인수 메서드는 호출될 때마다 배열을 새로 하나 할당하고 초기화하여 성능이 민감한 경우 문제가 될 수 있다.

  • 가변인수의 유연성이 필요할 때 선택할 수 있는 패턴

    public void foo() { }
    public void foo(int a1) { }
    public void foo(int a1, int a2) { }
    public void foo(int a1, int a2, int a3) { }
    public void foo(int a1, int a2, int a3, int... rest) { }

아이템 54 - null이 아닌, 빈 컬렉션이나 배열을 반환하라

  • 컬렉션이나 배열 같은 컨테이너가 비었을 때 null을 반환하는 메서드를 사용할 때면 항시 방어 코드를 넣어줘야 한다.
  • 빈 컨테이너를 할당하는 데도 비용이 드니 null을 반환하는게 성능상 낫다는 주장도 있으나 틀린 주장이다.
    • 이 정도의 성능 차이는 신경 쓸 수준이 못 된다.
    • 빈 컬렉션과 배열을 굳이 새로 할당하지 않고도 반환할 수 있다.
  • 빈 컬렉션 할당이 성능을 눈에 띄게 떨어뜨리는 방법
    • Collections.emptyList, Collections.emptySet, Collections.emptyMap
  • 배열을 쓸 대도 절대 null을 반환하지 말고 길이가 0인 배열을 반환하라.

아이템 55 - 옵셔널 반환은 신중히 하라

옵셔널의 장점

옵셔널을 반환하는 메서드는 예외를 던지는 메서드보다 유연하고 사용하기 쉬우며, null을 반환하는 메서드보다 오류 가능성이 작다.

옵셔널 사용 시 주의점

  • 옵셔널을 반환하는 메서드에서는 절대 null을 반환하지 말자, 옵셔널을 도입한 취지를 완전히 무시하는 행위다.

  • 반환값으로 옵셔널을 사용한다고 무조건 득이 되는 건 아니다. 컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안된다.

옵셔널을 메서드 반환 타입으로 사용해야 하는 경우

결과가 없을 수 있으며 클라이언트가 이 상황을 특별하게 처리해야 하는 경우

  • 박싱된 기본 타입을 담은 옵셔널을 반환하지 말고 int, long, double 전용 옵셔널 클래스인 OptionalInt, OptionalLong, OptionalDouble을 사용하자.

  • 옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는게 적절한 상황은 거의 없다.

아이템 56 - 공개된 API 요소에는 항상 문서화 주석을 작성하라

  • API를 쓸모 있게 하려면 잘 작성된 문서도 곁들여야 한다.
  • API를 올바로 문서화하려면 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에 문서화 주석을 달아야 한다.
  • 메서드용 문서화 주석에는 해당 메서드와 클라이언트 사이의 규약을 명료하게 기술해야 한다.
    • how가 아닌 what을 기술해야 한다.
  • 자기사용 패턴은 자바 8에 추가된 @impleSpec 태그로 문서화한다.
  • 열거 타입을 문서화할 때는 상수들에도 주석을 달아야 한다.
  • 애너테이션 타입을 문서화할 때는 멤버들에도 모두 주석을 달아야 한다.
  • 클래스 혹은 정적 메서드가 스레드 안전하든 아니든, 스레드 안전 수준을 반드시 API 설명에 포함해야 한다.