Java/Effective Java 3E

[이펙티브자바 3판] ITEM54. null이 아닌, 빈 컬렉션이나 배열을 반환하라

잭피 2021. 1. 16. 12:37

이번장의 핵심은...

null이 아닌, 빈 배열이나 컬렉션을 반환합시다

null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어납니다

렇다고 성능이 좋은 것도 아닙니다


null을 반환하지 말자

 

따라 하지 말아야하는 예제

private final List<Cheese> cheeseInStock = ...;
/**
* @return 매장 안의 모든 치즈가 목록을 반환한다
* 단, 재고가 없다면 null 반환
*/
public List<Cheese> getCheese() {
	return cheesesInStock.isEmpty() ? null 
						: new ArrayList<>(cheesesInStock);
}

→ null을 리턴해버리면 클라이언트는 이 null 상황을 처리하는 코드를 추가로 작성해야합니다

(클라이언트들은 계속 방어처리를 해야하므로 코드가 복잡하고 혹시나 빼먹으면 오류가 발생할 수 있습니다)

 

List<Cheese> cheese = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON))
	System.out.println("ok");

때로는 빈 컨테이너를 할당하는 데도 비용이 드니 null 반환하는 쪽이 낫다라는 주장이 있기도 합니다

하지만 두 가지 면에서 틀립니다

1. 성능 분석 결과 이 할당이 성능 저하의 주범이라고 확인되지 않는 한, 이정도의 성능 차이는 신경 쓸 수준이 못 됨

2. 빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환할 수 있다

// 빈 컬렉션을 반환하는 올바른 예
public List<Cheese> getCheese() {
	return new ArrayList<>(cheesesInStock);
}

가능성은 작지만, 사용 패턴에 따라 빈 컬렉션 할당이 성능을 눈에 띄게 떨어트릴 수 있습니다

-> 매번 똑같은 빈 '불변' 컬렉션을 반환하여 해결할 수 있습니다

ex) Collections.emptyList, Collections.emptySet, Collections.emptyMap

// 빈 컬렉션을 매번 새로 할당하지 않음
public List<Cheese> getCheese() {
	return cheesesInStock.isEmpty() ? Collections.emptyList() 
			: new ArrayList<>(cheesesInStock);
}

 

배열을 쓸 때도 null이 아닌 길이가 0인 배열을 반환할 수 있습니다

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

성능을 떨어트릴 것 같다면 아래처럼 해결할 수 있습니다

// 길이 0짜리 배열을 미리 선언해두고 매번 그 배열을 반환
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

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

-> cheesesInStock이 비었을 때면 언제나 EMPTY_CHEESE_ARRAY를 반환하게 됩니다

 

단순히 성능을 개선할 목적이라면 toArray에 넘기는 배열을 미리 할당하는 건 추천하지 않습니다

오히려 성능이 떨어진다는 연구 결과도 있습니다

 

나쁜 예로는 아래 코드처럼 배열을 미리 할당하면 성능이 나빠집니다

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