Ответ 1
Итак, я придумал следующее решение:
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
, который закроет любой зарегистрированный Closeable
, когда он будет закрыт. Чтобы упростить реализацию, я делегирую этот интерфейс простой реализации на основе списка.
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()
}