티스토리 뷰
다음은 주변에서 흔히 볼 수 있는 메서드입니다.
private final List<Cheese> cheeseInStock = ...;
/**
* @return 매장 안의 모든 치즈 목록을 반환한다.
* 단, 재고가 하나도 없다면 null을 반환한다.
*/
public List<Cheese> getCheeses() {
return cheeseInStock.isEmpty() ? null
: new ArrayList<>(cheeseInstock);
}
사실 재고가 없다고 해서 특별한 취급할 이유는 없습니다.
그럼에도 이 코드처럼 null을 반환한다면, 클라이언트 역시 이 null 상황을 처리하는 코드를 추가로 작성해야 합니다.
List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON)) {
System.out.println("좋았어, 바로 그거야.");
}
컬렉션이나 배열 같은 컨테이너(container)가 비었을 때 null을 반환하는 메서드를 사용할 때면 항시 이와 같은 방어 코드를 넣어줘야 합니다.
클라이언트에서 방어 코드를 빼먹으면 오류가 발생할 수 있습니다.
실제로 객체가 0개일 가능성이 거의 없는 상황에서는 수년 뒤에야 오류가 발생하기도 합니다.
한편, null을 반환하려면 반환하는 쪽에서도 이 상황을 특별히 취급해줘야 해서 코드가 더 복잡해집니다.
때로는 빈 컨테이너를 할당하는 데도 비용이 드니 null을 반환하는 쪽이 더 낫다는 주장이 있습니다.
하지만 이는 두 가지 면에서 틀린 주장입니다.
첫 째로, 성능 분석 결과 이 할당이 성능 저하의 주범이라고 확인되지 않는 한(아이템 67) 이 정도의 성능 차이는 신경 쓸 수준이 못 됩니다.
둘 째로, 빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환할 수 있습니다.
다음은 빈 컬렉션을 반환하는 전형적인 코드로, 대부분의 상황에서는 이렇게 하면 됩니다.
public List<Cheese> getCheeses() {
return new ArrayList<>(cheesesInStock);
}
가능성은 작지만, 사용 패턴에 따라 빈 컬렉션 할당이 성능을 눈에 띄게 떨어뜨릴 수도 있습니다.
다행히 해법은 간단합니다.
매번 똑같은 빈 '불변' 컬렉션을 반환하는 것이죠.
불변 객체는 자유롭게 공유해도 안전합니다. (아이템 17)
다음 코드에서 사용하는 Collections.emptyList 메서드가 그러한 예입니다.
집합이 필요하면 Collections.emptySet을, 맵이 필요하면 Collections.emptyMap을 사용하면 됩니다.
단, 이 역시 최적화에 해당하니 꼭 필요할 때만 사용합시다.
최적화가 필요하다고 판단되면 수정 전과 후의 성능을 측정하여 실제로 성능이 개선되는지 꼭 확인합시다.
public List<Cheese> getCheeses() {
return cheeseInStock.isEmpty() ? Collections.emptyList()
: new ArrayList<>(cheeseInstock);
}
배열을 쓸 때도 마찬가지입니다.
절대 null을 반환하지 말고 길이가 0인 배열을 반환합시다.
보통은 단순히 정확한 길이의 배열을 반환하기만 하면 됩니다.
그 길이가 0일 수도 있을 뿐입니다.
다음 코드에서 toArray 메서드에 건넨 길이 0짜리 배열은 우리가 원하는 반환 타입을 알려주는 역할을 합니다.
public Cheese[] getCheeses() {
return cheeseInStock.toArray(new Cheese[0]);
}
이 방식이 성능을 떨어뜨릴 것 같다면 길이 0짜리 배열을 미리 선언해두고 매번 그 배열을 반환하면 됩니다.
길이 0인 배열은 모두 불변이기 때문입니다.
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
return cheeseInStock.toArray(EMPTY_CHEESE_ARRAY);
}
이 최적화 버전의 getCheeses는 항상 EMPTY_CHEESE_ARRAY를 인수로 넘겨 toArray를 호출합니다.
따라서 cheeseInStock이 비었을 때면 언제나 EMPTY_CHEESE_ARRAY를 반환하게 됩니다.
단순히 성능을 개선할 목적이라면 toArray에 넘기는 배열을 미리 할당하는 건 추천하지 않습니다.
오히려 성능이 떨어질수도 있다고 합니다.
// 나쁜 예 - 배열을 미리 할당하면 성능이 나빠진다.
return cheeseInStock.toArray(new Cheese[cheeseInStock.size()]);
정리
- null이 아닌, 빈 배열이나 컬렉션을 반환하라.
- null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어난다.
- 그렇다고 성능이 좋은 것도 아니다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
아이템[56]. 공개된 API 요소에는 항상 문서화 주석을 작성하라 (0) | 2022.05.07 |
---|---|
아이템[55]. 옵셔널 반환은 신중히 하라 (0) | 2022.05.07 |
아이템[53]. 가변인수는 신중히 사용하라 (0) | 2022.05.07 |
아이템[52]. 다중정의는 신중히 사용하라 (0) | 2022.05.07 |
아이템[51]. 메서드 시그니처를 신중히 설계하라 (0) | 2022.05.02 |
- Total
- Today
- Yesterday
- 이펙티브 자바
- C++
- 백준
- kotest
- 클린 코드
- Olympiad
- 알고리즘
- JPA
- AWS
- 디자인 패턴
- 정규표현식
- 이팩티브 자바
- Effective Java
- node.js
- programmers
- Java
- kkoon9
- 프로그래머스
- 객체지향
- Spring Boot
- 클린 아키텍처
- MSA
- 디자인패턴
- 테라폼
- Algorithm
- Kotlin
- BAEKJOON
- 코테
- BOJ
- Spring
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |