Лучший способ реализовать делегат переназначаемого свойства - PullRequest
0 голосов
/ 09 марта 2019

Я подал заявку на переназначение собственности делегата

internal object UNINITIALIZED_VALUE

class SynchronizedReassignableImpl<out T>(private val initializer: () -> T,
                                          private val expiredPredicate: (T) -> Boolean,
                                          lock: Any? = null) : Reassignable<T> {
    @Volatile
    private var _value: Any? = UNINITIALIZED_VALUE
    private val lock = lock ?: this
    override val value: T
        get() {
            if (!isExpired()) {
                @Suppress("UNCHECKED_CAST") (_value as T)
            }
            return synchronized(lock) {
                val _v2 = _value
                @Suppress("UNCHECKED_CAST")
                if (_v2 !== UNINITIALIZED_VALUE && !expiredPredicate.invoke(_value as T)) {
                    _v2 as T
                } else {
                    val typedValue = initializer()
                    _value = typedValue
                    typedValue
                }
            }
        }

    @Suppress("UNCHECKED_CAST")
    override fun isExpired(): Boolean = !isInitialized() || expiredPredicate.invoke(_value as T)
    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
    override fun toString(): String = if (isInitialized()) value.toString() else "Reassignable value not initialized yet."
    operator fun getValue(any: Any, property: KProperty<*>): T = value
    operator fun getValue(any: Nothing?, property: KProperty<*>): T = value
}

fun <T> reassignable(initializer: () -> T, expiredPredicate: (T) -> Boolean, lock: Any? = null): SynchronizedReassignableImpl<T> {
    return SynchronizedReassignableImpl(initializer, expiredPredicate, lock)
}

interface Reassignable<out T> {
    val value: T
    fun isInitialized(): Boolean
    fun isExpired(): Boolean
}

Этот код объявляет, что свойство Delegate работает как ленивый, но при каждом вызове получателя будет вызываться предикат для определения состояния значения (истек или нет). Если значение истекло, оно будет переназначено.

Работает, например

class SynchronizedReassignableImplTests {
    @Test
    fun isReassignable() {
        val initializer = { mutableListOf<String>() }
        val expiredPredicate = { l: List<String> -> l.size == 2 }
        val list by reassignable(initializer, expiredPredicate)
        Assertions.assertEquals(0, list.size)
        list.add("item ${list.size}")
        Assertions.assertEquals(1, list.size)
        list.add("item ${list.size}") // list size is 2 on next getter's call it will be reassigned
        Assertions.assertEquals(0, list.size)
        list.add("item ${list.size}")
        Assertions.assertEquals(1, list.size)
    }
}

но я работаю с Kotlin всего два дня и думаю, что мое решение не очень красиво. Может кто-нибудь дать мне совет, чтобы сделать это? А может, у Котлина есть нативное решение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...