Post

(Kotlin) 한 번만 초기화 되는 필드 - Delegates.initOnlyOnce

Delegates.initOnlyOnce

어떤 변수가,

  1. 컴파일 타임 초기화 -> 불가능
  2. 앱 시작 시점에 초기화 -> 불가능
  3. 런타임 중간에 초기화 -> 가능 이면서 딱 한번만 초기화 되어야 하는 경우가 있다.

이런 패턴 적용하는데 적합한건 Delegate인데, standard Delegates 중에 제공되는건 없다.

“최초 null로 초기화 하고, 현재 상태가 null이 아니면 다른 값으로 초기화는 불가능” 한 Delegate를 직접 작성해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private class InitOnlyOnce<T: Any>(): ReadWriteProperty<Any?, T> {
    private var value: T? = null

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        check(this.value == null) {
            "Property ${property.name} can't be initialized more than once."
        }
        this.value = value
    }
}

fun <T : Any> Delegates.initOnlyOnce(): ReadWriteProperty<Any?, T> = InitOnlyOnce()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class InitOnlyOnceTest {
    @Test
    fun testInitOnlyOnce() {
        val a = A()
        assertThrows<IllegalStateException> { a.prop }
        a.prop = 1
        assert(a.prop == 1)
        assertThrows<IllegalStateException> { a.prop = 2 }
        assert(a.prop == 1)
    }

    class A {
        var prop by Delegates.initOnlyOnce<Int>()
    }
}

사례

Spring Batch 잡 파라미터

잡을 실행하는데 필요한 파라미터를 모아 VO로 관리하는데, VO 필드 초기화를 위한 setter 호출은 일반적으로 JobExecutionListener.beforeJob에서 수행. (Spring Batch) JobParameter 관리

This post is licensed under CC BY 4.0 by the author.