Kotlin. 지연

Tnalxmsk·2024년 1월 9일

Kotlin

목록 보기
7/8

개발 시 인스턴스를 생성하고, 프로퍼티에 값을 할당하는 등의 작업을 진행합니다. 인스턴스를 생성하고 프로퍼티를 초기화하는 방법으로 생성자를 통해 이루어지거나, Setter 메서드를 통해 값을 할당하는 등의 다양한 방법을 이용합니다.

종종 인스턴스 생성 시 프로퍼티 값 또는 상태를 정의하기 어려울 때, 초기 값으로 null을 할당하여, 필요한 시점에 값을 할당합니다. 그러나 이런 방법은 코틀린에서 권장하지 않는 방법입니다. 코드의 길이가 많지 않을 땐 초기화되지 않은 프로퍼티가 무엇인지 파악할 수 있지만 규모가 커질수록 놓칠 가능성이 커질 수 있습니다. 또한 코틀린에서는 null 처리(?, ?:, !!)를 계속 해주어야 합니다.

그렇다면 위 같은 상황에 어떻게 대응할 수 있을까요?

코틀린은 인스턴스화 시점과 프로퍼티의 초기화 시점을 분리해주는 방법을 제공하며, 이를 통해 수월하게 대응할 수 있습니다. 또한 단위 테스트 시에도 수월하게 코드를 작성하여 테스트를 진행할 수 있습니다.

지연 초기화 - lateinit

코틀린은 lateinit이라는 키워드를 제공하며, 이를 통해 인스턴스화 시점과 프로퍼티의 초기화 시점을 분리할 수 있습니다. lateinit의 대표적인 사용 예로 안드로이드의 Binding 코드를 보여드리겠습니다.

class HomeActivity : AppCompatActivity() {
    private lateinit var binding: ActivityHomeBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityHomeBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

Android 개발 프레임워크에선 개발자가 Activity, Fragemnt와 같은 클래스를 직접 인스턴스화 하거나, 파라미터, setter를 통해 값을 대입할 수 없습니다. 그래서 lateinit 이라는 키워드를 사용해 Activity가 onCreate() 상태가 되었을 때 binding 변수를 초기화하는 작업을 진행하여 setContentView() 함수의 인자로 전달합니다.

lateinit 키워드 사용 시 주의할 점은 컴파일 시점에서 lateinit 키워드가 붙은 변수는 nullable 변수입니다. 또한 접근하려 할 때 null 이라면 예외가 발생합니다. null이 아니라면 정상적으로 값을 반환합니다.

이제 우리는 lateinit을 통해 효과적으로 인스턴스화 시점과 프로퍼티 초기화 시점을 분리할 수 있습니다. 그러나 값의 비용이 크거나, 해당 변수가 잘 사용되지 않을 수도 있습니다. 이런 상황에 대응하기 위하여 지정된 로직을 1회만 실행시키고 싶다면 어떻게 해야할까요?

Backing Property

위에서 제시한 문제 상황을 해결하기 위해 backing property를 사용할 수 있습니다.

private var _table: Map<String, Int>? = null
val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // Type parameters are inferred
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

table이 사용되는 경우에만 _table을 초기화 한 후 get() { … } 로직을 실행하여 table에 값을 초기화합니다.

하지만 get() { … } 내부 코드를 다른 변수에 계속 작성하는 건 반복적이고 지루한 작업입니다. 이러한 문제를 by lazy를 사용하여 해결할 수 있습니다.

by lazy

바로 코드를 통해 알아봅시다.

class Person {
    val name: String by lazy { 
        Thread.sleep(3000)
        "김두루미"
    }
}

lazy는 지연 대리자 속성(lazy delegate properties)입니다. lazy는 lateinit과 달리 var가 아닌 val 키워드를 사용합니다. val 변수이기 때문에 값 변경이 불가능하죠. lazy는 람다 함수를 통해 변수의 초기화를 진행합니다. 람다 함수에는 여러 구문이 작성될 수 있고, 맨 마지막 구문의 결과가 변수에 할당되어 초기화됩니다.

0개의 댓글