[Effective Kotlin] 아이템8. 적절하게 null을 처리하라

Jimin Lim·2023년 7월 17일
0

Effective Kotlin

목록 보기
8/39
post-thumbnail

아이템 8

적절하게 null을 처리하라

✅ null의 의미

  • 프로퍼티의 null: 값이 설정되지 않았음 or 제거됨
  • 함수의 null
    • String.toIntOrNull(): String을 Int로 적절하게 변환할 수 없는 경우
    • Iterable<T>.firstOrNull(() -> Boolean): 주어진 조건에 맞는 요소가 없을 경우 null

위와 같이 null은 최대한 명확한 의미를 갖도록 하는 것이 좋다.

✅ null 처리

null 의 경우, 아래와 같이 세가지 방법으로 처리할 수 있다.

  1. ?., 스마트 캐스팅, Elvis 연산자 등을 활용해서 안전하게 처리
  2. 오류 throw
  3. 함수, 프로퍼티를 리팩터링해서 nullable 타입이 나오지 않도록

🔗 null을 안전하게 처리하기

printer?.print() //safe call
if (printer != null) printer.print() //smart casting

//Elvis 연산자
val printName1 = printer?.name ?: "Unnamed"
val printName2 = printer?.name ?: return
val printName3 = printer?.name ?: throw Error("에러")

🔗 오류 throw

위의 방식들은 개발자에게 오류를 알리지 않고, 코드가 진행되는 방법이다. 만약 null일때 문제가 발생한다면 개발자에게 오류를 강제로 발생시켜주는 것이 좋다.

이때 throw, !!, requireNotNull, checkNotNull 을 사용해 강제로 오류를 발생시킬 수 있다.

🔗 not-null assertion(!!)

!!은 null이 나오지 않는다는 것이 거의 확실한 상황에서 많이 사용된다. 하지만 미래에 null이 가능성은 언제나 존재한다. 따라서 결국 NPE 예외로 이어질 수 있다.
또한 계속해서 언팩(unpack)해야해서 귀찮아질 수 있고, 의미있는 null 값을 가질 가능성을 차단해버리게 된다.

!!은 웬만하면 지양하는 것이 좋고, lateinit, Delegates.notNull을 사용하는 것이 좋다.

  • lateinit, Delegates.notNull 둘 다 lazy init

🔗 의미없는 nullability 피하기

의미가 없다면 null을 사용하지 않는 것이 좋다. 이유 없이 사용한다면 !!을 사용하게 되고, 이는 의미 없이 코드를 더럽히게 된다.

nullability 를 피하는 방법은 다음과 같다.

  • 어떤 값이 클래스 생성 이후 확실히 설정된다는 보장이 있다면 lateinint 이나 Delegates.notnull을 사용하라
  • 빈 컬렉션 대신 null을 리턴하지마라. null은 컬렉션 자체가 없다는 것을 나타내며, 요소가 부족하다는 것은 빈 컬렉션으로 나타내라
  • nullable enum과 None enum 값은 완전히 다른 의미다. null enum은 별도로 처리해야 하지만, none enum은 정의에 없으므로 필요한 경우에 사용하는 쪽에서 추가해서 활용할 수 있다는 의미다.

🔗 lateinit 프로퍼티와 notNull 델리게이트

클래스가 클래스 생성 중 초기화할 수 없는 프로퍼티를 가질 수 있는데, 이는 사용 전 반드시 초기화해서 사용해야 한다. (ex. @BeforeEach)

lateinit은 처음 사용하기 전 반드시 초기화된다는 것을 의미한다. nullable과 비교해 다음과 같은 차이가 있다.

  • !! 로 인해 언팩하지 않아도 된다.
  • 이후 의미있는 null을 사용하기 위해 nullable로 사용할 수 있다.
  • 프로퍼티가 초기화된 이후에는 초기화되지 않은 상태로 돌아갈 수 없다.

notNull 델리게이트는 JVM에서 Int, Long, Double, Boolean과 같은 기본 타입과 연결된 타입으로 프로퍼티를 초기화해야 할 때 사용한다. (lateinit 보다 느리다)

 private var doctorId: Int by Delegates.notNull()

reference

lateinit, Delegates.notNull: https://codechacha.com/ko/diff-between-deligate-and-lateinit-in-kotlin/

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

좋은 글 감사합니다!

답글 달기