Kotlin 尝试使用资源/使用/多个资源

Kotlin 尝试使用资源/使用/多个资源,kotlin,Kotlin,我正在使用一个JavaAPI,它大量使用自动关闭接口,因此在Java中尝试使用资源。但是,在Java中,您可以指定 尝试(res1、res2、res3…){ ... } 我们有办法使用多种资源吗?它看起来像众所周知的回调地狱: val database=Databases.openDatabase(dbFile) 数据库使用{ database.createResource(ResourceConfiguration.Builder(resPathName,config.build()) va

我正在使用一个JavaAPI,它大量使用自动关闭接口,因此在Java中尝试使用资源。但是,在Java中,您可以指定

尝试(res1、res2、res3…){
...
}
我们有办法使用多种资源吗?它看起来像众所周知的回调地狱:

val database=Databases.openDatabase(dbFile)
数据库使用{
database.createResource(ResourceConfiguration.Builder(resPathName,config.build())
val resMgr=database.getResourceManager(ResourceManagerConfiguration.Builder(resPathName.build())
resMgr.use{
val wtx=resMgr.beginnodewriterx()
wtx.use{
wtx.insertSubtreeAsFirstChild(xmlshedder.createStringReader(resFileToStore))
}
}
}

没有标准的解决方案。如果您一开始就准备好了所有的
Closable
实例,那么您可以使用自己的自定义方法来处理它们,比如或show(这是官方论坛上的讨论导致了后者)

但是,在您的情况下,如果后续对象依赖于前面的对象,则这些对象都不会像常规的
try with resources
那样适用


我唯一能建议的是,尝试为自己定义隐藏嵌套的
use
调用的助手函数,如果可能的话,立即将您置于这些资源获取的第二层/第三层/第n层。

为简单起见,我将对链接的自动关闭项使用A、B和C。

import java.io.Closeable

open class MockCloseable: Closeable {
    override fun close() = TODO("Just for compilation")
}
class A: MockCloseable(){
    fun makeB(): B = TODO()
}
class B: MockCloseable(){
    fun makeC(): C = TODO()

}
class C: MockCloseable()
使用 这看起来像这样:

A().use {a ->
    a.makeB().use {b -> 
        b.makeC().use {c -> 
            println(c)
        }
    }
}
使用包装器生成链使用函数 定义

class ChainedCloseable<T: Closeable>(val payload: T, val parents: List<Closeable>) {
    fun <U> use(block: (T)->U): U {
        try {
            return block(payload)
        } finally {
            payload.close()
            parents.asReversed().forEach { it.close() }
        }
    }

    fun <U: Closeable> convert(block: (T)->U): ChainedCloseable<U> {
        val newPayload = block(payload)
        return ChainedCloseable(newPayload, parents + payload)
    }
}

fun <T: Closeable, U: Closeable> T.convert(block:(T)->U): ChainedCloseable<U> {
    val new = block(this)

}

这允许您避免以创建包装器对象为代价进行深度嵌套。

另一种方法是:

val CloseableContext = ThreadLocal<MutableList<AutoCloseable>>()

inline fun scopeDef(inScope: () -> Unit) {
    val oldContext = CloseableContext.get()

    val currentContext = mutableListOf<AutoCloseable>()

    CloseableContext.set(currentContext)

    try {
        inScope()
    }
    finally {
        for(i in (currentContext.size - 1) downTo 0) {
            try {
                currentContext[i].close()
            }
            catch(e: Exception) {
                // TODO: Record as suppressed exception
            }
        }
        CloseableContext.set(oldContext)
    }
}

fun <T: AutoCloseable> autoClose(resource: T): T {
    CloseableContext.get()?.add(resource) ?: throw IllegalStateException(
            "Calling autoClose outside of scopeDef is forbidden")

    return resource
}
  • 方法1:对于两个资源,使用本机java资源管理器

    /**
     * Based on https://medium.com/@appmattus/effective-kotlin-item-9-prefer-try-with-resources-to-try-finally-aec8c202c30a
     * and with a few changes
     */
    inline fun <T : Closeable?, R> Array<T>.use(block: (Array<T>) -> R): R {
        var exception: Throwable? = null
    
        try {
            return block(this)
        } catch (e: Throwable) {
            exception = e
            throw e
        } finally {
            when (exception) {
                null -> forEach { it?.close() }
                else -> forEach {
                    try {
                        it?.close()
                    } catch (closeException: Throwable) {
                        exception.addSuppressed(closeException)
                    }
                }
            }
        }
    }
    
  • 在Kotlin中定义
    jUsing()

    // crossinline version:
    inline fun <R, A : Closeable?, B : Closeable?>
            jUsing(a: A, b: B, crossinline block: (A, B) -> R): R = 
        J.jUsing(a, b) { c, d -> block(c, d) }
    
    Function2
    kotlin.jvm.functions.Function2

  • 然后像下面这样使用:

    // Download url to destFile and close streams correctly:
    jUsing(URL(url).openStream(), FileOutputStream(destFile), InputStream::transferTo)
    
    注意:上面的
    code
    使用了Java9+
    InputStream.transferTo()
    方法。有关与以前版本兼容的
    transferTo()
    Kotlin替代方案,请参阅


  • 注意:您可以使用
    noinline
    关键字而不是
    crossinline
    更简单地编写Kotlin
    jUsing()
    方法。但我认为,
    交叉内联
    版本具有更高的性能:

    有关更多详细信息,请参阅


我不知道,但没有什么能阻止您编写自己的自定义扩展函数,该函数借鉴了用户的想法。
// crossinline version:
inline fun <R, A : Closeable?, B : Closeable?>
        jUsing(a: A, b: B, crossinline block: (A, B) -> R): R = 
    J.jUsing(a, b) { c, d -> block(c, d) }
public static <R, A extends AutoCloseable, B extends AutoCloseable> R 
jUsing(A a, B b, Function2<A, B, R> block) throws Exception {
    try (a; b) {
        return block.invoke(a, b);
    }
}
// Download url to destFile and close streams correctly:
jUsing(URL(url).openStream(), FileOutputStream(destFile), InputStream::transferTo)
// noinline version:
inline fun <R, A : Closeable?, B : Closeable?>
        jUsing(a: A, b: B, noinline block: (A, B) -> R): R =
        Util.jUsing(a, b, block)
/**
 * Based on https://github.com/FelixEngl/KotlinUsings/blob/master/Usings.kt
 * and with some changes
 */
inline fun <R, A : Closeable, B : Closeable> using(a: A, b: B, block: (A, B) -> R): R {
    var exception: Throwable? = null

    try {
        return block(a, b)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        if (exception == null) {
            a.close()
            b.close()
        } else {
            try {
                a.close()
            } catch (closeException: Throwable) {
                exception.addSuppressed(closeException)
            }
            try {
                b.close()
            } catch (closeException: Throwable) {
                exception.addSuppressed(closeException)
            }
        }
    }
}
/**
 * Based on https://medium.com/@appmattus/effective-kotlin-item-9-prefer-try-with-resources-to-try-finally-aec8c202c30a
 * and with a few changes
 */
inline fun <T : Closeable?, R> Array<T>.use(block: (Array<T>) -> R): R {
    var exception: Throwable? = null

    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when (exception) {
            null -> forEach { it?.close() }
            else -> forEach {
                try {
                    it?.close()
                } catch (closeException: Throwable) {
                    exception.addSuppressed(closeException)
                }
            }
        }
    }
}