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

이진호·2022년 9월 5일
0

Effective Java

목록 보기
4/11
post-thumbnail

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

다음은 좋지 못한 코드의 예시입니다.

private final List<Cheese> cheesesInStock = ...;

/**
 * 재고가 없다면 null을 반환.
 */
public List<Cheese> getCheese() {
    return cheeseInStock.isEmpty() ? null : new ArrayList<>(cheeseInStock);
}

컬렉션이나 배열 같은 컨테이너(container)가 비었을 때 null을 반환하는 메서드의 경우, 클라이언트에서 방어 코드를 빼먹으면 오류가 발생할 수 있습니다. 또한 null을 반환하는 쪽에서도 이 상황을 특별히 취급해줘야 해서 코드가 더 복잡해지게 됩니다.

List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON))
    ...

한편 빈 컨테이너의 할당에도 비용이 드니 null을 반환하는 쪽이 낫다는 주장도 있습니다. 그러나 이것은 2가지 측면에서 틀린 주장입니다.

  1. 분석 결과 이 할당이 성능 저하의 주범이라고 확인되지 않는 한(Item 67), 이 정도의 성능 차이는 신경 쓸 수준이 못됩니다.
  2. 빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환할 수 있습니다. 대부분의 상황에서는 아래와 같이 하면 됩니다.
    public List<Cheese> getCheese() {
        return new ArrayList<>(cheeseInStock);
    }

사용 패턴에 따라 빈 컬렉션 할당이 성능을 눈에 띄게 떨어뜨릴 수도 있습니다. 하지만 이런 경우 매번 똑같은 빈 '불변'컬렉션을 반환하면 간단히 해결이 가능합니다. 불변 객체는 자유롭게 공유해도 안전합니다(Item 17). Collections.emptyList, Collections.emptySet, Collections.emptyMap이 대표적인 예시입니다.

배열을 쓸때도 마찬가지입니다. 절대 null을 반환하지 말고 길이가 0인 배열을 반환하면 됩니다.

public Cheese[] getCheese() {
    return cheeseInStock.toArray(new Cheese[0]);
}

이 방식이 성능을 떨어뜨릴 것 같다면 길이 0짜리 배열을 미리 선언해두고 매번 그 배열을 반환하면 됩니다. 길이 0인 배열은 모두 불변입니다.

private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheese() {
    return cheeseInStock.toArray(EMPTY_CHEESE_ARRAY);
}

<T> T[] List.toArray(T[] a) 메서드는 주어진 배열 a가 충분히 크면 a안에 원소를 담아 반환하고, 그렇지 않으면 T[] 타입 배열을 새로 만들어 그 안에 원소를 담아 반환합니다.

단순히 성능을 개선할 목적이라면 toArray에 넘기는 배열을 미리 할당하는 건 추천하지 않습니다. 오히려 성능이 떨어진다는 연구 결과도 있습니다.

return cheeseInStock.toArray(new Cheese[cheeseInStock.size()]);

핵심 정리

null이 아닌, 빈 배열이나 컬렉션을 반환하라. null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어난다. 그렇다고 성능이 좋은 것도 아니다.

출처

0개의 댓글