我正在使用Java-API,它大量使用Autoclosable-Interface,因此使用Java try-with-resources。但是在Java中,您可以指定
try (res1, res2, res3...) {
...
}
我们有办法使用多个资源吗?它看起来像众所周知的回调 - 地狱:
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))
}
}
}
答案 0 :(得分:5)
没有标准的解决方案。如果您在开始时已准备好所有Closable
个实例,则可以使用自己定义的方法来处理它们,例如this blog post或this repository个展示(以及here是导致后者的官方论坛的讨论。)
但是,在您的情况下,后续对象依赖于之前的对象,这些对象都不像常规try-with-resources
那样适用。
我唯一可以建议的是尝试为自己定义帮助函数,隐藏嵌套的use
调用,并立即将您置于这些资源收购的第二/第三/第n层,如果这样做的话。尽可能。
答案 1 :(得分:2)
另一种方法:
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
}
用法:
class Close1(val name: String): AutoCloseable {
override fun close() {
println("close $name")
}
}
fun main(args : Array<String>) {
scopeDef {
val c1 = autoClose(Close1("1"))
scopeDef {
val c3 = autoClose(Close1("3"))
}
val c2 = autoClose(Close1(c1.name + "+1"))
}
}
输出:
close 3
close 1+1
close 1
答案 2 :(得分:1)
为简单起见,我将使用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)
}
<强>用法强>
A()
.convert(A::makeB)
.convert(B::makeC)
.use { c ->
println(c)
}
这样可以避免深度嵌套,但代价是创建包装器对象。
答案 3 :(得分:0)
方法1:对于两个资源并使用本地Java资源管理器 :
在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) }
还有Util.jUsing()
中的Util.java
:
注意:code
以下与Java 9+兼容。您可以使用try-catch-finally
来实现它,使其与以前的版本兼容。有关示例,请参见here。
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);
}
}
({Function2()
是kotlin.jvm.functions.Function2
。)
然后使用,如下所示:
// Download url to destFile and close streams correctly:
jUsing(URL(url).openStream(), FileOutputStream(destFile), InputStream::transferTo)
注意:在code
上方使用Java 9+ InputStream.transferTo()
方法。有关与先前版本兼容的transferTo()
Kotlin替代方案,请参见here。
注意:您可以使用jUsing()
关键字而不是noinline
来更简单地编写Kotlin crossinline
方法。但是我认为crossinline
版本具有更高的性能:
// 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)
方法2:对于两个资源(与方法1 相似的用法):
/**
* 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)
}
}
}
}
方法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)
}
}
}
}
}
有关更多详细信息,请参见referenced link。