|| null 대신 빈 배열이나 빈 컬렉션을 반환하라.
|| null 을 반환한다고 해서 성능이 좋아지는 것도 아니고,
|| 오히려 작성해야 하는 오류 처리 코드만 늘어나기 때문이다.
메서드에서 null 이 반환되면 생기는 일
private final List<Cheese> cheesesInStock = ... ;
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}
List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON))
System.out.println("good");
• getCheeses 메소드를 호출하는 쪽에서는 NullPointerException 을 피하기 위해 반환된 값이 null 인지의 여부를 매번
체크해줘야 한다.
◦ 이렇게 오류가 발생하는 것을 방지하기 위해 추가적으로 작성해주는 코드를 방어코드 라고 부른다.
◦ 위 상황을 보면 불필요한 작업이 2개나 이루어지고 있다.
- getCheeses 메소드 : 반환하려는 대상의 Empty 여부를 체크하고 True 인 경우 null 반환
- getCheeses 메소드를 호출하는 쪽 : 메소드에서 반환된 값이 null 인지의 여부를 체크해 조건 처리
null 대신 빈 배열/컬렉션을 반환하는 경우
public List<Cheese> getCheeses() {
return new ArrayList<>(cheesesInStock);
}
• cheesesInStock 에 값이 들어 있으면 그 값으로 ArrayList 가 구성되어 반환되고, 비어있으면 Empty ArrayList 가 반환된다.
◦ cheesesInStock 이 Empty 인지 확인하는 절차를 생략할 수 있다.
◦ 당연히 getCheeses 메소드를 호출하는 쪽에서도 null 이 반환되지 않으니 null 을 처리하는 과정을 생략할 수 있다.
null 대신 빈 배열/컬렉션을 반환하는 것이 더 좋은 이유
• 빈 배열/컨테이너를 굳이 만들어서 반환하는데에도 비용이 발생하기 때문에 null 을 반환하는 것이 낫다는 주장도 있다.
• 하지만 아래 두가지 이유 때문에 이는 틀린 주장이라고 할 수 있다.
1) null 대신 빈 배열/컬렉션을 반환하는 경우의 성능 차이는 아주 미미하다.
• 분석 결과, 이 정도의 할당으로 인해 발생하는 성능 차이는 대부분 신경 쓸 수준이 되지 못한다.
2) 빈 배열/컬렉션은 굳이 새로 할당하지 않고도 반환할 수 있다.
• 만에 하나 빈 배열/컬렉션을 반환하는 행위가 성능 차이의 주범이 된다고 해도, 매번 새로운 배열/컬렉션을 할당하여 반환하는 대신
빈 불변 배열/컬렉션을 하나 만들어두고, 매번 이 똑같은 객체를 반환 시킴으로써 간단하게 해결할 수 있다.
public List<Cheese> getCheeses() {
return cheeseInStock.isEmpty() ? Collections.emptyList() : new ArrayList<>(cheeseInStock);
}
|| Collections.emptyList : 빈 불변 리스트를 반환하는 메소드
|| Collections.emptySet : 빈 불변 집합(Set)을 반환하는 메소드
|| Collections.emptyMap : 빈 불변 맵(Map)을 반환하는 메소드
• 단 이 역시 최적화에 해당하니 꼭 필요할 때에만 사용하고,
사용한 경우에는 수정 전과 후의 성능을 측정하여 실제로 성능이 개선 되었는지 여부를 꼭 확인하자.
public Cheese[] getCheeses() {
return cheesesInStock.toArray(new Cheese[0]);
}
// 또는 이렇게 작성할수도 있다.
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);
• 컬렉션 말고 배열을 사용하는 경우에는 위 코드처럼 구현할 수 있다.
◦ toArray 메소드에 파라미터로 넣어준 Cheese[0] 배열은 반환 타입을 지정해주는 역할
◦ 이 방식이 성능을 떨어뜨릴 것 같다면 이 역시 길이가 0인 배열(불변)을 미리 선언해두고 매번 그 배열을 사용하면 된다.
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
• 단, toArray 메소드에 넘기는 배열을 미리 할당해 두는 것이 오히려 성능 저하의 원인이 된다는 연구 결과도 있으므로,
정말 필요한 상황이 아닌 단순히 성능을 개선할 목적이라면 이 방식은 지양하도록 하자.
'Java' 카테고리의 다른 글
[Effective Java] ITEM64 : 객체는 인터페이스를 사용해 참조하라 (0) | 2022.09.20 |
---|---|
[Effective Java] ITEM59 : 라이브러리를 익히고 사용하라 (0) | 2022.09.06 |
[Effective Java] ITEM49 : 매개변수가 유효한지 검사하라 (0) | 2022.08.06 |
[Effective Java] ITEM44 : 표준 함수형 인터페이스를 사용하라 (feat.람다) (0) | 2022.08.06 |
[Effective Java] ITEM39 : 명명 패턴보다 Annotation을 사용하라 (0) | 2022.08.06 |