[이펙티브자바 3판] ITEM54. null이 아닌, 빈 컬렉션이나 배열을 반환하라
이번장의 핵심은...
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()]);