null 처리
package com.java.effective.item54;
import java.util.ArrayList;
import java.util.List;
public class Main {
private final static List<Cheese> cheesesInStock = new ArrayList<>();
/**
* 재고가 없다면 null 반환
* @return
*/
public static List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}
public static void main(String[] args) {
}
enum Cheese {
STILTON
}
}
재고가 없다면 null이 반환된다. 이 null 처리는 해당 메서드를 호출하는 클라이언트의 몫이다.
package com.java.effective.item54;
import java.util.ArrayList;
import java.util.List;
public class Main {
private final static List<Cheese> cheesesInStock = new ArrayList<>();
/**
* 재고가 없다면 null 반환
* @return
*/
public static List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}
public static void main(String[] args) {
List<Cheese> cheeses = getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON)) {
System.out.println("ok");
}
}
enum Cheese {
STILTON
}
}
null 상황을 처리하는 방어 코드가 추가되었다. 클라이언트에서 방어 코드를 빼먹으면 오류가 발생할 수도 있다. 실제로 객체가 0개일 가능성이 거의 없는 상황에서는 숨겨진 오류가 존재하는 것이다.
해결방안 1. 빈 리스트 반환
null이 아닌 빈 배열이나 빈 컬렉션을 반환하자. 빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환이 가능하다.
/** 기존 코드 */
public static List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}
/** 변환 코드 */
public static List<Cheese> getCheeses() {
return new ArrayList<>(cheesesInStock);
}
빈 컬렉션을 반환하는 코드로 변환하였다. 하지만 사용 패턴에 따라 빈 컬렉션 할당이 성능을 눈에 띄게 떨어뜨릴 수도 있다. 이때 해법이 존재하는데, 매번 똑같은 빈 '불변' 컬렉션을 반환하는 것이다. 불변 객체는 자유롭게 공유해도 안전하다.
이 예로, Collections.emptyList 메서드를 확인해보자.
/** 기존 코드 */
public static List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}
/** 변환 코드 */
public static List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? Collections.emptyList() : new ArrayList<>(cheesesInStock);
}
emtpyList() 코드를 한번 파악해보자.
public static final List EMPTY_LIST = new EmptyList<>();
...
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
...
이 코드는 최적화에 해당하므로, 꼭 필요할때만 사용해야한다. EMPTY_LIST는 불변 객체로, 안전하게 사용 가능하다. emptyList() 외에도 각 타입에 맞게 emptySet(), emptyMap()을 사용하면 된다.
해결방안 2. 빈 배열 반환
배열의 경우에도 절대 null을 반환하지 말고 길이가 0인 배열을 반환하자. 보통은 단순히 정확한 길이의 배열을 반환하기만 하면 된다. 그 길이가 0일수도 있다.
/** 변환 코드 */
public static Cheese[] getCheese() {
return cheesesInStock.toArray(new Cheese[0]);
}
toArray 메서드에 건넨 길이 0짜리 배열은 우리가 원하는 반환타입을 알려주는 역할도 동시에 한다. 이 방식이 성능을 떨어뜨릴것 같다면 길이 0짜리 배열을 미리 선언해두고 그 배열을 반환하면 된다. 길이 0인 배열은 모두 불변이기 때문이다.
private final static Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
...
public static Cheese[] getCheese() {
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
...
단순히 성능을 개선할 목적이라면 toArray에 넘기는 배열을 미리 할당하지 말자. 아래의 코드는 나쁜 예시다.
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);
'Book > Effective Java' 카테고리의 다른 글
[교재 EffectiveJava] 아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라 (0) | 2021.11.28 |
---|---|
[교재 EffectiveJava] 아이템 55. 옵셔널 반환은 신중히 하라 (0) | 2021.11.26 |
[교재 EffectiveJava] 아이템 53. 가변인수는 신중히 사용하라 (0) | 2021.11.24 |
[교재 EffectiveJava] 아이템 52. 다중정의는 신중히 사용하라 (0) | 2021.11.23 |
[교재 EffectiveJava] 아이템 51. 메서드 시그니처를 신중히 설계하라 (0) | 2021.11.22 |