아이템 7. 결과 부족이 발생할 경우 null과 Failure를 사용하라

네트워크 문제가 발생하거나 리스트에 첫 번째 요소가 없거나 파싱해서 객체를 만드는데 형식이 맞지 않는 경우 다음과 같이 처리할 수 있다.

  • null 또는 실패를 나타내는 sealed class 반환(일반적으로 Failure라는 이름)

  • 예외를 throw

이펙티브 자바에선 다음과 같은 이유로 예외는 잘못된 상황을 나타내야 하며 정보를 전달하는 방법으로 사용하면 안된다고 한다.

  • 많은 개발자가 예외 전파 과정을 제대로 추적하지 못한다.

  • 코틀린의 모든 예외는 unchecked exception이다.

  • 예외는 예외적인 상황을 처리하기 위해 만들어져서 명시적인 테스트(explicit test)만큼 빠르지 않다.

  • try-catch 블록 내부 코드는 컴파일러가 하는 최적화가 제한된다.

null 또는 Failure는 명시적이고 효율적이고 간단해서 예상되는 오류를 표현할때 좋다. 따라서 충분히 예측할 수 있는 범위의 오류는 null 또는 Failure를 사용하고 예측하기 어려운 오류는 예외를 throw해서 처리하는 것이 좋다.

null을 처리하면 사용자는 안전 호출(safe call) 또는 Elvis 연산자같은 널 안정성(null-safety) 기능을 활용할 수 있다.

val age = userText.readObjectOrNull<Person>()?.age ?: -1

Result로 처리하면 when 표현식을 사용해서 처리할 수 있다.

val person = userText.readObjectOrNull<Person>()
val age = when(person) {
  is Success -> person.age
  is Failure -> -1
}

sealed class Result<out T>
class Success<out T>(val result: T): Result<T>()
class Failure(val throwable: Throwable): Result<Nothing>()

예외는 놓칠수도 있고 전체 애플리케이션을 중지시킬 수도 있다. 반면 null과 sealed Result 클래스는 명시적으로 처리해야하고 애플리케이션을 중지시키지도 않는다.

따라서 다음과 같은 기준으로 오류를 처리한다.

  • 오류와 함께 추가적인 정보 전달 O -> sealed Result 클래스

  • 오류와 함께 추가적인 정보 전달 X -> null

일반적으로 두 가지 형태의 함수를 사용한다. 예상할 수 있을 때 get 을, 예상할 수 없을 땐 getOrNull 을 사용한다.

  • get : 특정 위치의 요소를 추출할 때 사용. 요소가 없다면 IndexOutOfBoundsException 발생

  • getOrNull : out of range 오류가 발생하면 null을 리턴

일반적으로 getOrNull 또는 Elvis 연산자를 사용하는 것이 쉽다.

개발자는 항상 요소를 안전하게 추출할거라 생각하기 때문에 nullable을 리턴하면 안된다. getOrNull 등을 사용해 무엇이 리턴되는지 예측할 수 있도록 해야 한다.

Last updated