본문 바로가기

Kotlin

[Kotlin] 5. Not-null assertion, let 함수, lateinit

# 널이 아님을 단언한다  !!

  • Nullable 타입 뒤에 !!(not-null assertion)을 붙이면 강제로 Non-Null  타입으로 변경된다
  • 이렇게 단언 해 놓고 null값이 들어가면 NullPointException이 발생함
  • 즉, 단언이 틀릴경우 Exception이 발생해도 감수하겠다는 뜻
  • !!을 사용하고 NPE가 발생했을 경우 에러가 발생한 Line수는 나오지만 어떤 식에서 예외가 발생했는지는 나오지 않는다.즉, 'students!!.subject!!.classtype' 처럼 한 줄에 단언문을 여러개 쓸 경우 어떤 값이  null이어서 발생한 에러인지 디버깅 하기 힘드니 한 줄에 단언문을 여러개 쓰는 경우는 피하자
  • 유의해서 사용해야 하며 꼭 필요한 경우에만 쓰도록 하자

간단 사용 예

fun getLength(str :String?):Int{
    return str!!.length
}

파라미터의 String값은 Nullable Type으로 설정 해놓고, Null이 아님을 단언해서 쓰고 있다

파라미터값으로 Null이 입력 되면 !! 단언문이 있는 부분에 NPE가 발생한다

 

 

그럼 어떤경우에 !!를 사용하는가?

사실상 str.length 호출에서 str이 null이 아님을 isStrEnabled()를 통해 확인 후 호출하고 있지만

컴파일러는 이를 알지 못하고 에러를 나타내고 있다 

이 때가 바로 not-null assertion이 필요한 때이다  str.length -> str!!.length 

 

# let 함수

  • 수신객체.let{ } 형태로 사용한다
  • 수신객체를 람다 안에 전달 해준다.
  • 1) 이 때 수신객체가 Nullable이고  ?. (세이프 콜)을 이용해서 let을 호출할 경우 수신객체가 Null이면 let 함수가 실행 되지 않을 것이고  Null이 아닐 경우 수신객체를 Non-Null 타입으로 변경해서 람다안에 전달 해 준다  
  • 2) Non-Null 타입으로 변경되었으므로, 람다 안에서는 Non-Null 타입을 파라미터로 받는 함수를 사용할 수 있다
  • 3) 수신 객체 사용 범위를 let 블록으로 제한 하는 의미도 있다
  • 4)마지막 값을 반환 한다
fun letTest(student: Student?) {
    //1) 안전한 호출 구문을 사용해서 let을 호출
    //   null이 아닐 경우에만 람다가 실행 되고 Non-Null Type의 Student로 바꿔서 람다에 전달 된다
    //   람다 안에서의 studen는 ?. 세이프 콜 없이 메소드를 호출하고 있는 것을 볼 수 있다.
    student?.let { student -> student.subject?.toString() }

    //2) let 함수는 마지막 값을 반환한다.
    val subjectStr: String? = student?.let { student -> student.subject?.toString() }

    //3)수신 객체 사용 범위를 let 블록으로 제한 하는 의미로도 사용 할 수 있다
    Student("sybang", Subject("Math", null)).let { student ->
        println(student.subject)
        println(student.name)
        student.xxx
        ....
        //student 관련 작업
        ....
    }
}

let을 중첩해서 사용할 경우 코드를 알아보기 힘들어 지며, if를 사용하는게 더 나은 경우도 있으니 고려하도록 하자!

 

# late-initialized lateinit 

  • 코틀린에서는 클래스 안의 Non-Null 프로퍼티는 생성자 안에서만 초기화 할 수 있다(일반적으로)
  • 하지만 생성자에서 초기화 할 수 없는 경우도 있는데 그럴 경우 무조건 Nullable 프로퍼티를 써야 하는가? Nullable 프로퍼티는 사용할 때마다 Null 체크가 필요하거나 !!를 사용해 Not Null 단언을 해야 하므로 코드가 번잡스럽...
  • Non-Null 특성이 분명한데, 단지 생성자에서 초기화 할 수 없는 경우에 Nullable로 사용 대신에 lateinit 변경자를 사용해서 프로퍼티를 나중에 초기화 한다
  • 나중에 초기화하는 프로퍼티는 무조건 var여야 한다 (val 프로퍼티는 final 이므로 꼭 생성자 안에서 초기화 돼야 함) 
  • 나중에 초기화하는 프로퍼티가 아직 초기화 되기 전에 접근하게 되면 예외가 발생한다(아직 초기화 되지 않았다는 Exception)
  • isInitialized 를 통해 초기화 되었는지 체크도 가능하다
class LateInitClass {
    lateinit var strVar: String

    fun initStr(str: String) {
        this.strVar = str
    }

    fun getStrLength(): Int {
        //초기화 됐는지 체크
        if (::strVar.isInitialized) {
            return strVar.length
        }else{
            return 0
        }
    }
}

 


잘못된 내용이나 궁금한 내용 있으시면 댓글 달아주세요

좋은 하루 되세요!

 

출처 : Kotlin In Action - 에이콘 출판사

(위 도서를 학습하고 개인 학습용으로 정리한 내용입니다)