공부/Kotlin

이펙티브 코틀린 1장 아이템7 - 결과 부족이 발생할 경우 null 과 Failure를 사용하라

데자와 맛있다 2024. 9. 8. 08:55

원하는 결과를 못 만들어내는경우가 있다.

예를들어

- 서버로 부터 데이터를 읽어올려고 햇는데 인터넷 연결 문제로 실패한경우

- 조건에 맞는 첫번째 요소를 찾으려 했는데 조건에 맞는 요소가 없는 경우

- 텍스트를 파싱해서 객체를 만들려고 했는데 텍스트의 형식이 맞지 않는 경우

 

예외 상황을 처리하는 방법은 다음과 같이 두개가 있다.

1. null 또는 실패를 나타내는 sealed 클래스 사용 (현재 내가 쓰고있는 방법이다)

-> 아래 2번 방법보다 알아보기 쉽고 효율적

2. 예외 throw

-> 놓칠 가능성이 있고 전체 애플리케이션을 중지시킬수 있다.

 

여기서 예외는 어떤 정보를 전달하는 역할을 하면 안된다. 오직 잘못된 상황을 나타내고 처리하는 용도로만 사용해야 한다.

이유

1. 예외가 전파되는 과정을 추적하기 어려움

2. 코틀린의 모든 예외는 unchecked예외이다 unchecked는 처리하지 않아도 실행이 되는 예외를 말함

3. 예외는 예외를 처리하기 위해서 만들어진것이므로 명시적인 테스트(?) 만큼 빠르지 않음

4. try - catch 안에 코드가 있으면 컴파일러가 할수있는 최적화가 제한된다

 

null, sealed 클래스 사용은 예상이 가능한 오류를 표현하기 좋다

따라서 예측이 가능한 오류는 1번 방법을 사용하고 예외가 예측 불가하면 2번 방법을 사용하는게 좋다


 

1번 방법에서 null 를 사용한다면 safe call 혹은 엘비스 연산자등을 사용해서 null처리를 해주자

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

공용체 (union type)을 사용하기로 했다면 when을 사용하자

val person = userText.readObjectOrNull<Person>()

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

 

null 과 union type 을 사용하는 상황의 차이점

- null

추가적인 정보를 전달할 필요 없을때

- union type

추가적 정보가 필요할때, 예를들어 Failure 라는 클래스에 어떤 변수를 추가해서 추가적으로 왜 실패되었는지 정보를 가지게 할수있음

 


앞에서 본 null / union type 둘중 하나를 사용한 예시가 List에 있다.

- get: 특정 인덱스에 있는 값을 꺼내는 함수, 그 인덱스가 리스트에 없다면 IndexOutOfBoundsException발생

- getOrNull: 특정 인덱스에 있는 값을 꺼낸다. 인덱스가 없으면 null 리턴, 일반적으로 이 함수랑 엘비스 연산자를 함께 사용하여 예외를 처리

 

이 외에도 getOrDefault 와 같은 선택지도 있다.

 

내 함수를 가져다 쓰는 사람(남이 되었건 내가되었건..) 에게 null이 리턴될수있다는 경고를 줘야 한다. 그런 경고 없이 nullable을 리턴하면 안된다 그래서 getOrNull등을 사용해서 어떤것이 리턴되는지 예측 가능하도록 하는것이 좋다