首页 > 解决方案 > 如何正确处理“使用”块中的异常?

问题描述

我希望Closeable资源自动关闭,但同时我希望能够处理任何可能的异常。

考虑以下代码。

val opcPackage: OPCPackage

URI.create(document.fileUrl).toURL().openStream().use {
    opcPackage = OPCPackage.open(it)  // it = InputStream!
}

如果出现异常,InputStream 将自动关闭,这要归功于该use块。但是,执行流程将被中断。

我仍然想处理这样的异常并对我的程序做一些事情(例如日志,发送信号 ecc ......)

以下不编译:

kotlin.runCatching {
    URI.create(document.fileUrl).toURL().openStream().use {
        opcPackage = OPCPackage.open(it)
    }
}.onFailure { 
    // e.g. log and exit
}

这种方式违背了整个目的,use因为我必须手动关闭Closeable.

URI.create(document.fileUrl).toURL().openStream().use {
    try {
        opcPackage = OPCPackage.open(it)
    } catch (ex: Exception) {
        // ...
        it.close()
        return internalServerError()
    }
}

在这一点上,我不妨简单地使用

val inputStream = URI.create(document.fileUrl).toURL().openStream()

try {
    opcPackage = OPCPackage.open(inputStream)
    inputStream.close()
} catch (ex: Exception) {
    inputStream.close()
    return internalServerError()
}

但我正在尝试正确使用 kotlin 功能。

这个用例的最佳片段是什么?

标签: kotlin

解决方案


use()允许您在一个表达式中处理资源管理,即使在异常情况下也是如此。你仍然可以try-catch绕过它:

val opcPackage = try {
    URI.create(document.fileUrl).toURL().openStream().use {
        OPCPackage.open(it)
    }
} catch (e: SomeException) { 
    // e.g. log and exit
    // no need to manually close here
    // you do need to break control flow here or provide a default OPCPackage
}

您的变体runCatching实际上也很好,您可能只需要将其用作表达式来初始化您的变量:

val opcPackageResult = runCatching {
    URI.create(document.fileUrl).toURL().openStream().use {
       OPCPackage.open(it)
    }
}.onFailure { 
    // e.g. log and exit
}

// you still need to handle the Result type later

但是即使你在里面捕获了异常,use()块的末尾仍然会为你关闭资源:

val opcPackage = URI.create(document.fileUrl).toURL().openStream().use {
    try {
        OPCPackage.open(it)
    } catch (ex: Exception) {
        return internalServerError()
    }
}

或者你也可以使用普通try-catch-finally的:

val inputStream = URI.create(document.fileUrl).toURL().openStream()

val opcPackage = try {
    OPCPackage.open(inputStream)
} catch (ex: Exception) {
    return internalServerError()
} finally {
    inputStream.close()
}

推荐阅读