일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |
- rxjava cold observable
- 안드로이드 os 구조
- android ar 개발
- 안드로이드 유닛 테스트
- ANR이란
- 안드로이드 라이선스 종류
- Rxjava Observable
- 안드로이드 레트로핏 crud
- 스택 큐 차이
- 서비스 쓰레드 차이
- rxjava hot observable
- 안드로이드 라이선스
- 플러터 설치 2022
- 멤버변수
- ar vr 차이
- 클래스
- android retrofit login
- 서비스 vs 쓰레드
- 안드로이드 유닛테스트란
- 객체
- 안드로이드 유닛 테스트 예시
- jvm이란
- rxjava disposable
- 2022 플러터 안드로이드 스튜디오
- 큐 자바 코드
- 안드로이드 레트로핏 사용법
- 자바 다형성
- jvm 작동 원리
- 2022 플러터 설치
- 스택 자바 코드
- Today
- Total
나만을 위한 블로그
[이펙티브 코틀린] 아이템 7. 결과 부족이 발생할 경우 null, Failure를 사용하라 본문
함수가 원하는 결과를 만들어 낼 수 없을 때가 있다.
- 서버에서 데이터를 읽어 들이려고 했는데 인터넷 연결 문제로 그러지 못한 경우
- 조건에 맞는 첫 번째 요소를 찾으려 했는데 없는 경우
- 텍스트를 파싱해서 객체를 만들려고 했는데 텍스트 형식이 맞지 않는 경우
이런 상황을 처리하는 매커니즘은 크게 2가지 있다.
- null 또는 '실패를 나타내는 sealed 클래스(일반적으로 Failure란 이름을 붙임)'를 리턴한다
- 예외를 throw한다
이 2가지는 중요한 차이가 있다. 예외는 정보를 전달하는 방법으로 쓰여선 안 된다.
예외는 잘못된 특별한 상황을 나타내야 하며 처리돼야 한다. 예외는 예외적인 상황이 발생했을 때 사용하는 게 좋다. 이 이유를 정리하면 아래와 같다.
- 많은 개발자가 예외가 전파되는 과정을 제대로 추적하지 못한다
- 코틀린의 모든 예외는 unchecked 예외다. 따라서 사용자가 예외를 처리하지 않을 수도 있으며 이와 관련된 내용은 문서에도 제대로 드러나지 않는다. 실제로 API를 쓸 때 예외 관련 사항을 단순하게 메서드 등을 사용하면서 파악하기 힘들다
- 예외는 예외적인 상황을 처리하기 위해 만들어졌으므로 명시적인 테스트(explicit test)만큼 빠르게 동작하지 않는다
- try-catch 블록 안에 코드를 배치하면 컴파일러가 할 수 있는 최적화가 제한된다
반면 null, Failure는 예상되는 오류를 표현할 때 굉장히 좋다. 이는 명시적이고 효율적이며 간단한 방법으로 처리 가능하다.
따라서 충분히 예측할 수 있는 범위의 오류는 null, Failure를 쓰고 예측하기 어려운 예외적인 범위의 오류는 예외를 throw해서 처리하는 게 좋다. 아래는 간단한 예시다.
inline fun <reified T> String.readObjectOrNull(): T? {
// ...
if (incorrectSign) {
return null
}
// ...
return result
}
inline fun <reified T> String.readObject(): Result<T> {
// ...
if (incorrectSign) {
return Failure(JsonParsingException())
}
// ...
return Success(result)
}
sealed class Result<out T>
class Success<out T>(val result: T): Result<T>()
class Failure(val throwable: Throwable): Result<Nothing>()
class JsonParsingException: Exception()
이렇게 표시되는 오류는 다루기 쉽고 놓치기 어렵다. null을 처리해야 한다면 사용자는 safe call 또는 엘비스 연산자 같은 다양한 null-safety 기능을 활용한다.
val age = userText.readObjectOrNull<Person>()?.age ?: -1
Result 같은 공용체(union type)를 리턴하기로 했다면 when 표현식을 써서 처리할 수 있다.
val person = userText.readObjectOrNull<Person>()
val age = when(person) {
is Success -> person.age
is Failure -> -1
}
이런 오류 처리 방식은 try-catch 블록보다 효율적이고 사용하기 쉽고 더 명확하다. 예외는 놓칠 수도 있고 전체 애플리케이션을 중지시킬 수도 있다. null 값과 sealed Result 클래스는 명시적으로 처리해야 하며 애플리케이션의 흐름을 중지하지도 않는다.
추가적인 정보를 전달해야 한다면 sealed Result 클래스를 쓰고 그게 아니면 null을 쓰는 게 일반적이다. Failure는 처리할 때 필요한 정보를 가질 수 있다.
일반적으로 2가지 형태의 함수를 쓴다. 하나는 예상할 수 있을 때, 다른 하나는 예상할 수 없을 때 사용한다.
- get : 특정 위치의 요소를 추출할 때 사용. 요소가 해당 위치에 없으면 IndexOutOfBoundsException 발생
- getOrNull : out of range 오류가 발생할 수 있는 경우 사용. 발생 시 null 리턴
이외에도 일부 상황에 유용한 getOrDefault 같은 다른 선택지도 있다. 하지만 일반적으로 getOrNull 또는 엘비스 연산자를 쓰는 게 쉽다.
개발자는 항상 자신이 요소를 안전하게 추출할 거라 생각한다. 따라서 nullable을 리턴하면 안 된다. 개발자에게 null이 발생할 수 있다는 경고를 주려면 getOrNull을 써서 뭐가 리턴되는지 예측할 수 있게 하는 게 좋다.
'책 > Effective Kotlin' 카테고리의 다른 글
[이펙티브 코틀린] 아이템 9. use를 써서 리소스를 닫아라 (0) | 2022.06.02 |
---|---|
[이펙티브 코틀린] 아이템 8. 적절하게 null을 처리하라 (0) | 2022.05.29 |
[이펙티브 코틀린] 아이템 6. 사용자 정의 오류보다는 표준 오류를 사용하라 (0) | 2022.05.26 |
[이펙티브 코틀린] 아이템 5. 예외를 활용해 코드에 제한을 걸어라 (0) | 2022.05.22 |
[이펙티브 코틀린] 아이템 4. inferred 타입으로 리턴하지 마라 (0) | 2022.05.15 |