본문 바로가기

Reading Record/이펙티브 코틀린

[이펙티브 코틀린] Item8. 적절하게 null을 처리하라

null을 안전하게 처리하기

  • 안전호출

      printer?.print()
  • 스마트 캐스팅

      if(printer != null) printer.print()
  • 엘비스 연산자

      printer?.name ?: "Unnamed"
      printer?.name ?: return
      printer?.name ?: throw Error("error")

Not-null assertion(!!)과 관련된 문제

  • !!문은 nullability가 제대로 표현되지 않는 라이브러리를 사용할 때 정도에만 사용해야함
  • 미래에 코드가 어떻게 변화할지 모르니 현재 null이 아니라고 !!문을 쓰면 안된다.
  • !! 연산자 사용은 최대한 피하자

의심 없는 nullability 피하기

  • nullability를 안전하게 처리할 수 있는 함수 사용
    • List getOrNull
  • 어떤 값이 클래스 생성 이후 확실하게 설정된다는 보장이 있으면 lateinit 프로퍼티와 notqnull 델리게이트를 사용
  • 빈 컬렉션 대신 null을 리턴하지 마세요
  • nullable enum vs none enum

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

  • lateinit 한정자는 프로퍼티가 이후에 설정될 것임을 명시하는 한정자
    • lateinit 선언이 되어있는데 초기화 안하고 사용시 예외발생
    • 장점
      • 언팩 하지 않아도 된다
      • 이후에 어떤 의미를 나타내기 위해서 null을 사용하고 싶을 때, nullable로 만들 수 있음
      • 프로퍼티가 초기화된 이후에는 초기화되지 않은 상태로 돌아갈 수 없다.
    • 단점
      • Int,Long,Double,Boolean과 같은 기본 타입에 사용 불가
      • Delegates로 대체
  • var text: String by Delegates.notNull()
private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}
  • notNull을 위임하면 get에서 널 검사를 진행한다.