Post

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

Delegates.initOnlyOnce

var 이면서 한 번만 초기화 될 수 있는 필드가 필요한 경우가 있다. (하단 사례 참조)
이런 패턴 적용하는데 적합한건 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>()
    }
}

사례

.properties를 담고 있는 Spring 영역 바깥의 Object

.propertiesyml에 있는 환경에 따라 달라지는 속성(프로퍼티)들을 가져올 때 (e.g., url)
DI로 가져오기 때문에, Bean이 아닌 DTO 같은 클래스에서는 url 속성 등에 접근 할 수 없다.
즉 이런 데이터는 반드시 Bean으로부터 넘겨 받아야 하는데, 이 점이 생각을 표현하는데 허들이 되는 경우가 종종 있다.

=> DTO 같은 Spring 영역 바깥에 존재하는 클래스에서도 자유롭게 참조 할 수 있는 Singleton 객체가 필요하다.

Spring 영역 바깥에서 singleton 만드려면 컴파일 타임에 생성되는 Object를 이용하게 되는데,
이런 프로퍼티들은 애플리케이션 시작 시점에 적절한 프로파일 별 .yml을 읽어와야 알 수 있으므로 컴파일 타임에 세팅되는 정적 데이터는 아니다.
따라서 Object.fieldvar 처리 할 수 밖에 없다.

var이므로 런타임에 언제든 변경 될 수 있다는 위험을 안고 있어, 이를 방지할 안전장치가 필요하다. => initOnlyOnce

Spring Batch 잡 파라미터

잡을 실행하는데 필요한 파라미터를 모아 VO로 관리하게 되는데
잡 파라미터는 보통 Job.afterStep에서 런타임에 처리하게 된다.

따라서 잡 파라미터 필드도 var 처리 할 수 밖에 없다. => 두번 초기화 되지 않게 initOnlyOnce

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