尝试使用资源/使用/多种资源

Joh*_*nes 8 kotlin

我正在使用Java-API,它大量使用Autoclosable-Interface,因此使用Java try-with-resources.但是在Java中您可以指定

try (res1, res2, res3...) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

我们有办法使用多种资源吗?它看起来像众所周知的回调 - 地狱:

val database = Databases.openDatabase(dbFile)

database.use {
  database.createResource(ResourceConfiguration.Builder(resPathName, config).build())

  val resMgr = database.getResourceManager(ResourceManagerConfiguration.Builder(resPathName).build())

  resMgr.use {
    val wtx = resMgr.beginNodeWriteTrx()

    wtx.use {
      wtx.insertSubtreeAsFirstChild(XMLShredder.createStringReader(resFileToStore))
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

zsm*_*b13 5

没有标准的解决方案.如果您Closable在开始时准备好了所有实例,那么您可以使用自己定义的方法来处理它们,例如此博客文章此存储库显示(这里是导致后者的官方论坛上的讨论).

然而,在你的情况下,后续对象依赖于之前的对象,这些都不像常规对象try-with-resources那样适用.

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


jrt*_*ell 5

为简单起见,我将使用 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()
Run Code Online (Sandbox Code Playgroud)

使用用途

这看起来像这样:

A().use {a ->
    a.makeB().use {b -> 
        b.makeC().use {c -> 
            println(c)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用包装器创建链使用函数

定义

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)

}
Run Code Online (Sandbox Code Playgroud)

用法

A()
    .convert(A::makeB)
    .convert(B::makeC)
    .use { c ->
         println(c)
    }
Run Code Online (Sandbox Code Playgroud)

这使您可以避免以创建包装对象为代价而进行深层嵌套。


Mir*_*ili 5

  • 方法1:对于两个资源并使用本机java资源管理器

    1. 在 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) }
      
      Run Code Online (Sandbox Code Playgroud)
    2. 还有:Util.jUsing()Util.java

      注意:以下code兼容 Java 9+。您可以使用它来实现它try-catch-finally以使其与以前的版本兼容。请参阅此处的示例。

      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);
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      Function2kotlin.jvm.functions.Function2。)

    3. 然后使用如下:

      // Download url to destFile and close streams correctly:
      jUsing(URL(url).openStream(), FileOutputStream(destFile), InputStream::transferTo)
      
      Run Code Online (Sandbox Code Playgroud)

      注意:上面code使用了Java 9+InputStream.transferTo()方法。请参阅此处,了解transferTo()与以前版本兼容的 Kotlin 替代方案。


    注意:您可以使用关键字而不是 来编写jUsing()更简单的 Kotlin 方法。但我认为版本有更多的性能:noinlinecrossinlinecrossinline

    // 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)
    
    Run Code Online (Sandbox Code Playgroud)

  • 方法 2:对于两个资源(与方法 1的用法类似)

    感谢@zsmb13 的回答提供的链接

    /**
     * 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)
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 方法 3:对于任意数量的资源 ( arrayOf(stream1, stream2, ...).use {...})

    /**
     * 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)
                    }
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    有关更多详细信息,请参阅参考链接。