null이 아닌, 빈 컬렉션이나 배열을 반환하라
null이 아닌 빈 배열이나 컬렉션을 반환하라.
null을 반환하는 API는 사용하기 어렵고, 오류 처리 코드도 늘어난다.
그렇다고 성능이 좋은 것도 아니다.
null 반환은 옳지 못하다
─ 컬렉션이 비었으면 null을 반환하는 코드 (옳지 못하다)
아래 메서드는 컬렉션이 빈 경우 null을 반환하게 두었다.
private final List<Cheese> cheesesInStock = ...;
/**
* @return 매장 안의 모든 치즈 목록을 반환한다.
* 단, 재고가 하나도 없다면 null을 반환.
*/
public List<Cheese> getCheeses(){
return cheesesInStock.isEmpty ? null
: new ArrayList<>(cheeseInStock);
}
이렇게 null을 반환하게 구현했다면,
반환한 null을 받는 클라이언트 측에서는 null을 처리하는 방어 코드를 항시 넣어 주어야 한다.
다음과 같이...
List<Chesse> cheeses = shop.getCheeses();
if (cheeses != null && chesses.contains(Cheese.STILTON))
System.out.println("~~~");
이렇게 null 상황을 특별하게 취급해 주어야 해서 복잡하고 피곤해진다.
빈 컬렉션/배열 반환의 올바른 예시
빈 컬렉션을 반환하는 올바른 예
↓ 대부분의 상황에서 이렇게 하면 된다.
public List<Cheese> getCheeses(){
return new ArrayList<>(cheesesInStock);
}
더 나은 방법이 있다.
"빈 컬렉션을 new 할당하지 않고, 불변 컬렉션으로"
"작은 가능성으로" 빈 컬렉션 할당이 성능을 떨어뜨리는 케이스가 있다. 그것이 성능 저하의 주범이며 최적화가 꼭 필요한 경우라면(Item67), 다음과 같이 해결 가능하다. 매번 똑같은 "비어있는 불변 컬렉션"을 반환하는 것이다.
불변 객체는 자유롭게 공유해도 안전하다(Item17)
Collections.emptyList(), Collections.emptySet(), Collections.emptyMap()를 리턴하면 된다.
public List<Cheese> getCheeses(){
return cheesesInStock.isEmpty() ? Collections.emptyList()
: new ArrayList<>(cheesesInStock);
}
빈 배열 반환의 올바른 예시
이또한 null 말고, 길이가 0인 배열을 반환하라.
public Cheese[] getCheeses(){
return cheesesInStock.toArray(new Cheese[0]);
}
만약 성능 저하가 우려된다면 길이 0인 배열을 미리 선언해두고 그 배열을 반환하면 된다.
└☆ 길이가 0인 배열은 모두 불변이기 때문이다.
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses(){
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
*참고: List.toArray(T[] a)에 대한 이해
위 코드가 잘 이해가지 않는다면, List의 toArray(T[] a)에 대한 이해가 필요한 것이다.
list를 array로 바꾸는 것인데, 인자의 array는 무슨 쓸모일까?
다음과 같이 동작한다.
∨ 인자의 array에 List 요소를 채운다. 마지막 요소 다음에는 null을 넣는다.
∨ list 사이즈 > array 사이즈라면, list길이의 새 array를 할당한다.
∨ list 사이즈 <= array 사이즈라면, 인자의 array에 list의 요소를 넣는다. 이때 array의 다른 공간엔 기존 array의 요소가 그대로 존재한다. (아래 첫번째 사진 참고!)
다음 캡쳐를 보면 이해가 될 것이다.
인자 arr의 길이에 따라 list.toArray(arr) 결과를 확인한 것이다.
또한 toArray(new Object[0])는 기능 면에서 toArray()와 동일하다.
이제 예제 코드를 다시 보면,
sheesesInStock이라는 리스트가 요소가 없을 때 (사이즈가 0일 때) 인 배열(new Cheese[0])을 반환하고,
리스트 요소가 하나 이상 존재할 때는, 그 요소를 담은 새로운 배열을 할당해 반환하게 된다.
public Cheese[] getCheeses(){
return cheesesInStock.toArray(new Cheese[0]);
}
※ 주의점
toArray(new Object[0])를 통해 배열이 새로 할당된다고 해서,
미리 직접 할당하는 것은 추천하지 않는다. 성능이 떨어진다는 연구 결과도 있다.
즉 다음과 같은 코드는 지양하자.
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);
'JAVA > Effective Java' 카테고리의 다른 글
[이펙티브 자바] 지역변수의 범위를 최소화하라 ─ 9장:일반적인 프로그래밍 원칙:Item57 (0) | 2023.10.19 |
---|---|
[이펙티브 자바] 옵셔널 반환은 신중히 하라 ─ 8장:메서드:Item55 (1) | 2023.10.18 |
[이펙티브 자바] 가변인수는 신중히 사용하라 ─ 8장:메서드:Item53 (0) | 2023.10.18 |
[이펙티브 자바] 다중정의(오버로딩)는 신중히 사용하라 ─ 8장:메서드:Item52 (1) | 2023.10.17 |
[이펙티브 자바] 메서드 시그니처를 신중하게 설계하라 ─ 8장:메서드:Item51 (0) | 2023.10.17 |