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

public List<Cheese> getCheeses() {
		return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheeseInStock);
}

위의 코드는 리스트에 값이 없으면 null 을 반환하는 코드이다. 주변에서 흔히 볼 수 있는 코드지만 null 을 반환한다면 이 메서드를 사용하는 측에서 다음과 같이 처리를 해주어야 한다.

List<Cheese> cheeses = shop.getCheese();
if (cheeses != null && cheeses.contains(Cheese.STILTON))
		System.out.println("좋았어, 바로 그거야.");

이와 같이 컬렉션이나 배열 같은 컨테이너(container)가 비었을 때 null 을 반환하는 메서드를 사용할 때면 매번 방어적인 코드를 작성해야 한다. 클라이언트에서 방어 코드를 빠트리면 오류(NPE) 가 발생할 수도 있다. 또, null 을 반환하는 쪽에서도 값이 없는 경우를 특별취급하여 처리해주어야 한다.

때로는 빈 컨테이너를 할당하는 데에도 비용이 드니 null 을 반환하는 쪽이 낫다는 주장도 있다. 하지만 이는 틀린 주장이다.

  1. 성능 분석 결과 이 할당이 성능 저하의 주범이라고 확인되지 않는 한(아이템 67) 이 정도의 성능 차이는 신경 쓸 수준이 못된다.
  2. 빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환할 수 있다.
public List<Cheese> getCheeses() {
		return new ArrayList<>(cheesesInStock);
}

가능성은 낮지만, 사용 패턴에 따라 빈 컬렉션 할당이 성능을 눈에 띄게 떨어뜨릴 수도 있다. 이 경우엔 매번 똑같은 빈 불변 컬렉션을 반환한다. 불변 객체는 자유롭게 공유해도 안전하다.(아이템 17)

public List<Cheese> getCheeses() {
		return cheesesInStock.isEmpty() ? Collections.emptyList() 
				: new ArrayList<>(cheesesInStock);

최적화하여 빈 컬렉션을 매번 새로 할당하지 않도록 하였다. 단, 최적화는 예상과는 다른 결과를 초래할 수 있으니 신중하는 것이 좋다.(아이템 67)

배열을 쓸때도 마찬가지로 null 이 아닌 길이가 0인 배열을 반환한다. 매번 새로운 빈 배열 객체를 생성하는 것이 부담스럽다면 다음과 같이 미리 선언해두고 재사용할 수 있다.

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

public Cheese[] getCheeses() {
		return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}