개인 광고 영역

Kotlin Late Initialized Properties와 lazy Properties에 대해서 정리하려고 합니다.

각각은 사용 방법과 용도가 다르지만 늦은 초기화에 대한 부분이 유사하다고 생각하여 함께 정리하려고 합니다.


Late-Initialized properties

lateinit 문서를 참고하면 junit test를 통해 lateinit을 익힐 수 있습니다.


lateinit을 소개하기 전에 java 변수 선언은 아래와 같습니다.

이 경우 subject는 null로 초기화됩니다.

class Test {
	String subject;
}

위의 코드를 kotlin에서 작성해보면 다음과 같습니다.

class Test {
	var subject: String
}

하지만 java는 null으로 초기화를 해주지만 kotlin에서는 아래와 같은 오류가 발생합니다.

Property must be initialized or be abstract

init {} 블록을 사용해 초기화하거나, abstract class 구성을 하라는 이야기입니다.

코틀린에서는 명시적으로 초기화를 하거나 var subject: String? = null이라고 명시해야 합니다.

class Test {
	var subject: String
	init {
		subject = ""
	}
}

또는 아래와 같이 초기화해야 합니다.

class Test {
	var subject: String = ""
	// 또는
	var subject = "" // 묵시적으로 String으로 유추 가능
}

위의 코드는 최신 버전의 kotlin에서는 2번째 방법으로 초기화하도록 유도하고 있습니다.

굳이 init block을 사용하여 초기화해줄 필요는 없기 때문이죠.


lateinit 정의

위에서와 같이 초기화할 수도 있지만 다음과 같이 lateinit을 이용한 초기화가 가능합니다.

이 경우 init {} 시에 초기화하는 것이 아닌 나중에 사용하는 시점에서 초기화할 수 있습니다.

class Test {
	lateinit var subject: String
}

lateinit 사용 시

  • var(mutable)만 사용 가능
  • null을 사용해서 초기화 불필요
  • 늦은 초기화이므로 초기화 전에 사용하면 오류 발생
    • lateinit property subject has not been initialized
  • 변수에 대한 setter/getter 사용할 수 없음
    • 해당 부분은 나중에 정리 예정입니다.


lateinit 사용법

lateinit을 사용하려면 아래와 같이 초기화하고, 이를 아래와 같이 사용할 수 있습니다.

fun main() {
	val test = Test()
	test.subject = "제목 초기화"
	println("subject ${test.subject}")
}

class Test {
	lateinit var subject: String
}

그래서 위와 같이 초기화 가능합니다.

null / ""으로 initialized 해줄 필요가 없습니다.


lazy properties

이번에는 lazy properties를 정리합니다.

lazy를 통해서 늦은 초기화를 진행하고, 이를 사용할 수 있습니다.

lazy는 아래와 같이 초기화합니다.

val subject: String by lazy {
	"제목 초기화"
}
// 또는
val subject by lazy {
	"제목 초기화"
}

lazy는

  • 호출 시점에 초기화를 진행합니다.
  • val(immutable)과 함께 사용합니다.

정리하면

lateinit의 경우는 var(mutable)이므로 값이 변경될 수 있지만, lazy은 val(immutable)이므로 변하지 않는 값을 초기화해야 할 경우 유용합니다.


lazy 초기화 확인해보기

lazy가 늦은 초기화라고 하였으니 실제로 늦은 초기화를 진행하는지 확인이 가능합니다.

그리고 한번 생성하면 이후에는 초기화가 일어나지 않아야겠죠.

fun main(args: Array<String>) {
    val test = Test()
    test.test()
}

class Test {
    init {
        println("init")
    }
    
    val subject by lazy {
        println("lazy initialized")
        "제목 초기화"
	}

    fun test() {
        println("not initialized")
        println("subject one : $subject")
        println("subject two : $subject")
        println("subject three : $subject")
    }
}

이를 실행하면 다음과 같음을 확인할 수 있습니다.

init
not initialized
lazy initialized
subject one : 제목 초기화
subject two : 제목 초기화
subject three : 제목 초기화

가장 첫 번째에서 lazy initialized을 호출하고, 이후에는 생성한 제목 초기화를 3번 불러오는 것을 확인할 수 있습니다.

추가로 팁

lazy는 람다식 블록입니다. 그렇기에 println()과 초기화 이름의 순서가 변경되는 경우 아래와 같이 결과가 다르게 나올 수 있습니다.

init
not initialized
lazy initialized
subject one : kotlin.Unit
subject two : kotlin.Unit
subject three : kotlin.Unit


lazy 사용하기

Android에서 lazy 사용은 findViewById와 함께 사용이 가능합니다.

class ImageActivity : AppCompatActivity() {

    private val toolbar by lazy {
        findViewById(R.id.toolbar) as Toolbar
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_image_sample)

        toolbar.setTitle(R.string.app_name)
        setSupportActionBar(toolbar)
    }
}

val toolbar by lazy {}을 사용하게 되면, 실제 toolbar 호출 시점인 setSupportActionBar(toolbar)에서 lazy 블록의 init 하고, 이를 return 합니다.

이 경우 val로 정의하고, null이 아닌 view를 사용하게 되어, kotlin에서 null 처리가 불필요합니다.

as Toolbar 역시 안전한 캐스트가 가능하도록 as? Toolbar 처리가 가능합니다.(이 경우는 null)


마무리

late-initialized-properties와 lazy delegated-properties에 대해서 정리하였습니다.

kotlin에서 늦은 초기화를 유용하게 사용하면 이쁜 코드 개발이 가능하고, 재미있는 코드를 만드는 것도 가능합니다.


kotlin 관련 글 더 보기


개인 광고 영역

Tae-hwan

Android, Kotlin .. Create a content development.