Kotlin 尝试使用资源/使用/多个资源
我正在使用一个JavaAPI,它大量使用自动关闭接口,因此在Java中尝试使用资源。但是,在Java中,您可以指定Kotlin 尝试使用资源/使用/多个资源,kotlin,Kotlin,我正在使用一个JavaAPI,它大量使用自动关闭接口,因此在Java中尝试使用资源。但是,在Java中,您可以指定 尝试(res1、res2、res3…){ ... } 我们有办法使用多种资源吗?它看起来像众所周知的回调地狱: val database=Databases.openDatabase(dbFile) 数据库使用{ database.createResource(ResourceConfiguration.Builder(resPathName,config.build()) va
尝试(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)
使用了Java9+code
方法。有关与以前版本兼容的InputStream.transferTo()
Kotlin替代方案,请参阅transferTo()
注意:您可以使用
noinline
关键字而不是crossinline
更简单地编写KotlinjUsing()
方法。但我认为,交叉内联
版本具有更高的性能:
有关更多详细信息,请参阅
我不知道,但没有什么能阻止您编写自己的自定义扩展函数,该函数借鉴了用户的想法。
// 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)
}
}
}
}
}