在Kotlin访问属性委托

Mic*_*ael 11 delegates properties kotlin

Kotlin委托了属性,这是一个非常好的功能.但有时get()set()方法是不够的.假设我想Closeable懒惰地创建一个对象并稍后关闭它.以下是如何实现此类委托属性的示例:

fun <T : Closeable> closeableLazy(initializer: () -> T) =
        CloseableLazyVal(initializer)

class CloseableLazyVal<T : Closeable>(
    private val initializer: () -> T
) : ReadOnlyProperty<Any?, T> {

    private var value: T? = null

    override fun get(thisRef: Any?, desc: PropertyMetadata): T {
        if (value == null) {
            value = initializer()
        }
        return value
    }

    fun close() {
        value?.close()
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我想用它的方式:

private val stream by closeableLazy { FileOutputStream("/path/to/file") }

fun writeBytes(bytes: ByteArray) {
    stream.write(bytes)
}

override fun close() {
    stream::delegate.close() // This line will not compile
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这种方法不起作用,因为Kotlin似乎不允许直接访问属性委托.有什么办法可以做我想要的吗?或者是否有任何计划将此类功能添加到Kotlin,因为它将是一个如此简洁的功能.

Kir*_*man 6

好的,所以我提出了以下解决方案:

fun <T : Closeable> closeableLazy(initializer: () -> T) =
        CloseableLazyVal(initializer)

class CloseableLazyVal<T : Closeable>(
        private val initializer: () -> T
) : ReadOnlyProperty<CloseableDelegateHost, T> {

    private var value: T? = null

    override fun get(thisRef: CloseableDelegateHost, desc: PropertyMetadata): T {
        if (value == null) {
            value = initializer()
            thisRef.registerCloseable(value!!)
        }
        return value!!
    }

}

interface CloseableDelegateHost : Closeable {
    fun registerCloseable(prop : Closeable)
}

class ClosableDelegateHostImpl : CloseableDelegateHost {

    val closeables = arrayListOf<Closeable>()

    override fun registerCloseable(prop: Closeable) {
        closeables.add(prop)
    }

    override fun close() = closeables.forEach { it.close() }
}

class Foo : CloseableDelegateHost by ClosableDelegateHostImpl() {
    private val stream by closeableLazy { FileOutputStream("/path/to/file") }

    fun writeBytes(bytes: ByteArray) {
        stream.write(bytes)
    }

}
Run Code Online (Sandbox Code Playgroud)

请注意,属性的get方法有一个参数thisRef.我要求它继承CloseableDelegateHost关闭任何已注册的Closeables.为了简化实现,我将此接口委托给一个简单的基于列表的实现.

UPDATE(从注释中复制):我意识到,您可以将委托声明为单独的属性,然后将第二个属性委托给它.这样您就可以轻松访问委托本身.

private val streamDelegate = closeableLazy { FileOutputStream("/path/to/file") }
private val stream by streamDelegate

fun writeBytes(bytes: ByteArray) {
    stream.write(bytes)
}

override fun close() {
    streamDelegate.close()
}
Run Code Online (Sandbox Code Playgroud)

  • 我意识到,您可以将委托声明为单独的属性,然后将第二个属性委托给它.这样您就可以轻松访问委托本身. (2认同)

Ing*_*gel 6

在Kotlin 1.1中(从beta 2开始),可以从属性中检索代理,因此您现在可以编写代码

override fun close() {
    (::stream.apply { isAccessible = true }.getDelegate() 
        as CloseableLazyVal<*>).close()
}
Run Code Online (Sandbox Code Playgroud)