https://velog.io/@evergreen_tree/KotlinDelegation
이전 글에 이어서, 이번에는 Delegated Property에 대해 포스팅하겠습니다. KProperty 라는 생소한 개념도 나와서 함께 작성해 보았습니다.
Character
라는 클래스에서 프로퍼티를 갱신하거나, 참조할 때 마다 값을 출력하는 상황을 예로 들어 보겠습니다.
class Character {
var hp: Int = 0
get() {
println("Get hp : $field")
return field
}
set(value) {
println("Get hp : $value")
field = value
}
var mp: Int = 0
get() {
println("Get hp : $field")
return field
}
set(value) {
println("Get mp : $value")
field = value
}
}
fun main() {
val character = Character()
character.hp = 100
character.mp = 200
println("캐릭터의 hp는 ${character.hp} mp는 ${character.mp}")
}
Character
라는 클래스 내에서 접근자를 작성해주고, main에서 프로퍼티를 변경해 준 후 결과를 보겠습니다.
그리 복잡하지 않은 코드이지만, 이 다섯줄을 출력하기 위해 소비하는 코드가 너무 많습니다. 또한 중복된 내용을 담고 있습니다.
코드를 보면 접근자의 동작이 같은 형식을 띄고 있기에, 이를 따로 클래스에 담아서 수작업으로 위임해보겠습니다.
class Delegator(private val name: String) {
var value: Int = 0
fun getter(): Int{
println("get $name : $value")
return value
}
fun setter(newValue: Int){
println("set $name : $value")
value = newValue
}
}
class Character {
var hpDelegator = Delegator("hp")
var mpDelegator = Delegator("mp")
var hp: Int
get() = hpDelegator.getter()
set(value) = hpDelegator.setter(value)
var mp: Int
get() = mpDelegator.getter()
set(value) = mpDelegator.setter(value)
}
fun main() {
val character = Character()
character.hp = 100
character.mp = 200
println("캐릭터의 hp는 ${character.hp} mp는 ${character.mp}")
}
결과는 전과 똑같습니다.
총 코드 라인 수는 그렇게 달라지지 않았지만, Delegator에게 공통 형식의 접근자를 만들고 위임함으로써 재사용성이 올라갔습니다. 하지만 아직까지 중복되는 코드가 많아보입니다.
이제 코틀린의 by
키워드를 통해 이를 더 줄여보겠습니다.
import kotlin.reflect.KProperty
class Delegator(private var value: Int) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
println("get ${property.name} : $value")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) {
println("set ${property.name} : $newValue")
value = newValue
}
}
class Character(hp: Int, mp: Int) {
var hp by Delegator(hp)
var mp by Delegator(mp)
}
fun main() {
val character = Character(0,0)
character.hp = 100
character.mp = 200
println("캐릭터의 hp는 ${character.hp} mp는 ${character.mp}")
}
Delegator
클래스에서 연산자 오버로딩을 통해, getValue
와 setValue
를 제공하게 되면, Character
클래스의 hp
와 mp
에 접근자를 위임받아 사용할 수 있게 됩니다.
getValue와 setValue 라는 연산자 오버로딩을 구현하기 위해서는, 지정된 파라미터를 꼭 제공해야 합니다.
getValue | setValue |
---|---|
thisRef : 위임을 사용하는 클래스와 같은 타입이거나 Any 타입이어야 한다 | thisRef : 위임을 사용하는 클래스와 같은 타입이거나 Any 타입이어야 한다 |
property : Property<*>거나 Any 타입이어야 한다. | property : Property<*>거나 Any 타입이어야 한다. |
newValue : 위임을 사용하는 프로퍼티와 같은 타입이거나 Any 타입이어야 한다 |
Kproperty
는 kotlin.reflect 패키지에 선언된 인터페이스로,val
orvar
로 선언된 프로퍼티의 정보를 가지는 인터페이스입니다.operator fun getValue(thisRef: Any?, property: KProperty<*>) { println("set ${property.name} : $newValue") }
만약
val hp
라면property.name
에서 “hp”에 대해 얻을 수 있습니다.KProperty
의 인스턴스는 Reflection 연산자인 ::으로부터 얻을 수 있습니다.
메이플이 하고싶은거양..?