Link
Today
Total
10-17 00:15
Archives
관리 메뉴

초보개발자 긍.응.성

(이펙티브 자바 3) 8장 메서드 정리 본문

책 정리/이펙티브 자바 3

(이펙티브 자바 3) 8장 메서드 정리

긍.응.성 2020. 12. 1. 22:40
반응형

매개변수가 유효한지 검사하라

메서드와 생성자 대부분은 입력 매개변수의 값이 특정 조건을 만족하기를 바란다. 메서드 몸체가 실행되기 전에 매개변수를 확인한다면 잘못된 값이 넘어왔을 때 즉각적이고 깔끔한 방식으로 예외를 던질 수 있다. 

public과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다.

자바 7에 추가된 java.utils.Objects.requireNonNull 메서드는 유연하고 사용하기도 편하니, 더 이상 null 검사를 수동으로 하지 않아도 된다.

this.strategy = Objects.requireNonNull(strategy, "전략");

단언문(assert)를 사용해 매개변수 유효성을 검증할 수 있다.

private static void sort(long a[], int offset, int length) {
	assert a != null;
	assert offset >= 0 && offset <= a.lenght;
	assert length >= 0 && length <= a.length - offset;
}

단언문은 유효성 검사와 아래의 다른점을 가진다

  • 실패하면 AssertionError를 던진다
  • 런타임에 아무런 효과도, 아무런 성능 저하도 없다

생성자 매개변수의 유효성 검사는 클래스 불변식을 어기는 객체가 만들어지지 않게 하는 데 꼭 필요하다.

적시에 방어적 복사본을 만들라

클라이언트가 여러분의 불변식을 깨뜨리려 혈안이 되어 있다고 가정하고 방어적으로 프로그래밍해야 한다. 외부 공격으로부터 인스턴스 내부를 보호하려면 생성자에서 받은 가변 매개변수 각각을 방어적으로 복사(defensive copy) 해야 한다. 또한 접근자에서는 가변 필드의 방어적 복사본을 반환하여 불변을 유지시키자. 복사 비용이 너무 크거나 클라이언트가 구성 요소를 잘못 수정할 일이 없음을 신뢰한다면 방어적 복사를 수행하는 대신 해당 구성요소를 수정했을 때의 책임이 클라이언트에 있음을 문서에 명시하도록 하자.

메서드 시그니처를 신중히 설계하라

  • 메서드 이름을 신중히 짓자
  • 편의 메서드를 너무 많이 만들지 말자
  • 매개변수 목록은 짧게 유지하자
    • 여러 메서드로 쪼갠다 - 직교성이 높도록 쪼개자
    • 매개변수 여러 개를 묶어주는 도우미 클래스를 만들자
    • 객체 생성에 사용한 빌더 패턴을 메서드 호출에 사용하자
  • 매개변수의 타입으로는 클래스보다는 인터페이스가 더 낫다
  • boolean보다는 원소 2개짜리 열거 타입이 낫다

다중정의는 신중히 사용하라

재정의한 메서드(Override)는 동적으로 선택되고, 다중정의한 메서드는 정적으로 선택된다. 다중정의된 메서드 사이에는 객체의 런타임 타입은 전혀 중요치 않다. 선택은 컴파일타임에, 오직 매개변수의 컴파일타임 타입에 의해 이뤄진다.

그렇기에 다중 정의가 혼동을 일으키는 상황을 피해야 한다. 안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말고 가변 인수(varargs)를 사용하는 메서드라면 다중정의를 아예 하지 말아야 한다. 다중정의하는 대신 메서드 이름을 다르게 지어주자.

생성자는 이름을 다르게 지을 수 없지만 정적 팩토리라는 대안이 존재한다. 대신, 매개변수 중 하나 이상이 근본적으로 다르다(radically different) 면 헷갈릴 일이 없다.

radically different - 두 타입의 (null이 아닌) 값을 서로 어느 쪽으로든 형 변환할 수 없다.

다중정의된 메서드들이 함수형 인터페이스를 인수로 받을 때, 비록 서로 다른 함수형 인터페이스라도 인수 위치가 같으면 혼란이 생긴다. 메서드를 다중정의할 때, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안 된다.

가변인수는 신중히 사용하라

가변인수 메서드는 호출될 때마다 배열을 새로 하나 할당하고 초기화한다. 성능을 생각한다면 일정 개수의 인수에 대해서는 가변인수가 아닌 다중정의 메서드를 만들어 주자.

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) {}

예를 들어 해당 메서드 호출의 95%가 인수를 3개 이하로 사용한다고 하면 메서드 호출 중 단 5%만이 배열을 생성한다.

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

컬렉션이나 배열 같은 컨테이너가 비었을 때 null을 반환하는 메서드를 사용할 때면 반드시 방어 코드를 넣어주어야 한다. 차라리 빈 컬렉션이나 배열을 반환하자. 빈 컨테이너를 할당하는 데도 비용이 든다는 주장이 있는데 이는 두 가지 면에서 틀린 주장이다.

  1. 성능 분석 결과 이 할당이 성능 저하의 주범이라고 확인되지 않는 한, 이 정도의 성능 차이는 신경 쓸 수준이 못 된다.
  2. 빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환할 수 있다 (매번 똑같은 빈 불변 컬렉션을 반환).

null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어난다. 그리고 성능이 좋은 것도 아니다.

옵셔널 반환은 신중히 하라

결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야 한다면 Optional<T>를 반환하자. OptionalInt, OptionalLong, OptionalDouble이 있기에 박싱 된 기본 타입을 담은 옵셔널은 쓰지는 말자. 컬렉션은 결과가 없을 수 있더라도 Optional에 담기보다는 빈 컬렉션을 반환하자. 또한 옵셔널을 반환하는 메서드에서는 절대 null을 반환하지 말자.

반응형
Comments