Kotlin has delegated properties which is a very nice feature. But sometimes get() and set() methods are not enough. Let's say I want to create a Closeable object lazily and to close it later. Here's an example of how such delegate property could be implemented:

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()
    }
}

这就是我想使用它的方式:

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
}

不幸的是,这种方法不起作用,因为Kotlin似乎不允许直接访问属性委托.有什么办法可以满足我的要求吗?或者有没有计划在Kotlin中添加这样的功能,因为这将是一个非常简洁的特性.

推荐答案

Ok, so I came up with the following solution:

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)
    }

}

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

UPDATE (copied from comments): I realized, you can just declare the delegate as a separate property and then delegate the second property to it. This way you can access the delegate itself easily.

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()
}

Kotlin相关问答推荐

Kotlin—列出具有不同T的列表之间的操作'

如何避免使用公共类实现内部接口

在调用父构造函数之前重写类属性

当我通过媒体通知更改音乐时不要更新我的 UI

如何更改默认推断没有接收者的函数类型?

扩展属性委托给实例属性

垂直滚动条下拉菜单的桌面组合

init中的NPE抽象函数变量

通用接口继承

在 Kotlin 中,我可以在集合上有一个条件构建器元素吗?

使用空键映射获取

Kotlin 插件之间的区别

Jetpack compose 可滚动表格

使用纯 Kotlin 函数作为 Junit5 方法源

Kotlin 静态函数:伴生对象,@JvmStatic @JvmField

如何捕获传递给模拟函数的参数并返回它?

修改扩展函数中的this

指定为非null的参数在ArrayAdaper中为null

如何为 Java 调用者声明返回类型为void的 Kotlin Lambda?

为什么 Kotlin 会收到这样的 UndeclaredThrowableException 而不是 ParseException?