||  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 메소드에 넘기는 배열을 미리 할당해 두는 것이 오히려 성능 저하의 원인이 된다는 연구 결과도 있으므로,

    정말 필요한 상황이 아닌 단순히 성능을 개선할 목적이라면 이 방식은 지양하도록 하자.