깨알 개념/Kotlin

[Kotlin] 위임 프로퍼티 (Delegated properties)

interfacer_han 2024. 7. 22. 22:45

#1 Delegated properties

#1-1 개요

 

Delegated properties | Kotlin

 

kotlinlang.org

위임 프로퍼티는, getter와 setter 로직을 다른 클래스에 위임(delegate)하는 코틀린 프로퍼티를 의미한다. 본 게시글에서는 위임 프로퍼티의 기제에 대해 탐구하고 또 위임 프로퍼티를 간단히 구현해본다.

 

#1-2 위임(delegate)의 의미

 

[Kotlin] 프로퍼티(Property)

#1 필드와 프로퍼티 #1-1 프로퍼티의 개념 // Java String name = "steve"; // Field System.out.println("my name is" + name); name = "kevin"; --- // Kotlin var name : String = "steve" // Property println("my name is" + name) name = "kevin" 코틀

kenel.tistory.com

위 게시글에서 보듯, 코틀린 프로퍼티는 명시적 get() 및 set() 구문으로 getter 및 setter를 커스텀할 수 있다. 이 커스텀 getter 및 setter는 해당 객체에 한정되어 적용된다. 따라서 모듈화를 통한 재사용이 불가능하다. 이 때 위임 프로퍼티를 사용할 수 있다.

 

그렇다면 위임이란 무슨 의미로 쓰인 단어인가? 어떤 클래스(이하 A 클래스) 타입인 프로퍼티 a가 있다고 해보자. a의 get() 및 set()을 커스텀하려고 한다. 위 링크에 있는 게시글처럼 get() 및 set()을 프로그래머가 직접 커스텀하는 방법이 있을 것이다. 이 때 그 대신 다른 클래스(이하 B 클래스)에 정의된 getter 및 setter를 가져와 a의 get() 및 set()에 대입하는 것이 바로 '위임'의 의미다. 다시 말하자면 어떤 객체의 값을 읽고 쓰는 동작을 직접 구현하지 않고, 다른 클래스에게 맡기는 것이다. 때문에 클래스 B는 읽고 쓰는 동작에 대한 연산자 오버로딩 메소드가 반드시 존재해야 한다. 그리고 그 연산자 오버로딩 메소드는 operator fun getValue() 및 operator fun setValue()으로 약속되어 있다.

 

주의할 점은, operator fun getValue() 및 operator fun setValue()는 위임 전용 연산자 오버로딩 메소드라는 것이다. 즉, 이 메소드를 가지고 있다고 해서 평소에도 해당 B 클래스가 이 클래스를 이용해 자신의 값을 get하거나 set하지는 는다는 얘기다. 오직 위임 프로퍼티에게 getter와 setter를 제공할 때 전달되는 목적으로 존재하는 메소드다. 다시 말하자면 B 클래스의 get()은 getValue()와 관련 없는 메소드고, 마찬가지로 B 클래스의 set()은 setValue()와는 다른 메소드다.

 

#1-3 위임(Delegate object) 객체: 위임 프로퍼티의 get 및 set을 대신하는 클래스

import kotlin.reflect.KProperty

// 클래스 B: 위임 객체(Delegate object)
class MyDelegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Delegated property"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Delegated property never set to $value")
    }
}

#1-2에서 말한 B 클래스의 가장 기본적인 형태다. B 클래스와 같은 역할을 수행하는 클래스를 위임 객체(Delegate object)라 한다. getValue() 반환형 및 setValue()의 매개변수 타입을 보아, 위 예시 코드는 #1-2에서 말한 A 클래스가 String일 때만 위임을 수행할 수 있을 것이다. MyDelegate에게 get() 및 set()을 위임한 String 프로퍼티가 존재한다면 그 프로퍼티의 값은 "Delegated property"로 영원히 고정되고, 아무리 값을 바꾸려는 시도를 해도 그 때마다 콘솔에 "Delegated property never set to $value"를 내뱉을 뿐 실제 값을 바꿀 수는 없을 것이다.

 

#1-4 위임 프로퍼티 선언 형식

val/var <property name>: <Type> by <expression>

#1-2에서 말한 프로퍼티 a는 <property name>을 의미한다. A는 <Type>에 대응된다. B는 <expression>에 대응된다. 이때 a는 val일 수도 있고 var일 수도 있는데, val인 경우 <expression>은 operator fun getValue()만을 갖춰도 된다. 하지만 var이라면 추가로 operator fun setValue()까지 갖춰야 <property name>을 위임 프로퍼티로 만들 수 있다.

 

#2 코드 예시

import kotlin.reflect.KProperty

// 클래스 B: 위임 객체(Delegate object)
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Delegated property"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Delegated property never set to $value")
    }
}

// 프로퍼티 a: 위임 프로퍼티(Delegated property)
var a: String by Delegate()

fun main() {
    println(a)
    a = "Hello!"
    println(a)
}

/* 출력 결과

Delegated property
Delegated property never set to Hello!
Delegated property

*/

 

#3 요약

재사용 가능한 getter/setter (= 위임 객체)로 위임 프로퍼티를 만들어낸다.