본문 바로가기

안드로이드/Kotlin

[Kotlin] 위임(Delegation)과 초기화 지연(lazy initialization)

읽기 전용 프로퍼티(Read-Only Property)

일반적으로 안드로이드 애플리케이션에서 대부분의 위젯 참조는 액티비티의 생명 주기와 동일하게 유지된다. 즉, 처음 할당된 참조를 바꿀 필요가 없어진다. 따라서, 이는 read-only인 val을 활용하면 된다.

 

하지만, val의 경우 선언과 동시에 값을 가져야 한다는 문제점이 생긴다. 아래와 같이 뷰를 참조해야될 경우 val을 통해 read-only로 참조하고 싶지만 뷰가 세팅 된 이후에 findViewById를 활용할 수 있기 때문에 불가능하다. 

class MainActivity : AppCompatAcitivity(){
    val textView: TextView //에러
    override fun onCreate(savedInstanceState: Bundle?) {
    	super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        //textView = findViewById(R.id.text_view) as TextView
    }
}
lateinit의 경우 var에 대해만 사용 가능하고 이는 read-only 프로퍼티가 아니다.
또한, Read-only는 참조가 바뀌지 않는다는 것을 명심.

 

by lazy를 통한 지연 초기화

by lazy를 통해 읽기 전용 프로퍼티에 대해 지연 초기화가 가능하다. 이를 사용하면 해당 프로퍼티가 사용되는 최초의 지점에서 자동으로 초기화 과정이 실행된다.

class MainActivity : AppCompatActivity(){
    private val messageView: TextView by lazy {
    	findViewById(R.id.text_view) as TextView
    }
    
    override fun onCreate(savedInstanceState:Bundle?) {
        super.onCreate(savedInstanceState)
    }
    
    fun onSayHello() {
        messageView.text = "Hello"
    }
}

 

Delegated Property

Delegation은 위임을 의미한다. A가 B에게 작업을 위임하면 B가 작업을 대신 처리해준다. Kotlin에서 위임은 다음과 같이 by <delegate> 형식으로 위임 프로퍼티(Delegated property)를 선언할 수 있다. 프로퍼티 위임은 프로퍼티에 대한 getter/setter를 위임하여 위임받은 객체가 값을 읽고 쓸 때 어떠한 행위를 수행하게 한다.

val/var '프로퍼티명' : '자료형' by <delegate>
import kotlin.reflect.KProperty

class PropertyDelegate(var value: String) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("${property.name} get value ${this.value}")

        return value
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
        println("${property.name} set value ${this.value} -> $newValue")

        this.value = newValue
    }
}

class Person {
    var name:String by PropertyDelegate("no name") // getValue, setValue
    val age: String by PropertyDelegate("31") // getValue
}

fun main() {
    val person = Person()
    // name get value no name
    println("person name is ${person.name}") // person name is no name

    person.name = "jin" // name set value no name -> jin
    // name get value no name
    println("person name is ${person.name}") // person name is jin

    // age get value 31
    println("person age is ${person.age}") // person age is 31
}