该问题与try-with-resources无关,只是编译后的代码与运行时环境不兼容的依赖问题。
问题是在编译时SQLiteDatabase 实现了接口Closeable,然后你将它换成另一个没有的实现。但是代码已经编译了,不知道这个变化。
当您调用函数/方法时,会检查所有参数以确保它们是正确的类型,这通常由 JVM 处理。对于内联和扩展函数,Kotlin 编译器插入类型检查以确保它在正确的类型上运行,对于这个特定的内联函数,函数的接收者被定义为Closeable。这是方法签名:
inline fun <T : Closeable, R> T.use(block: (T) -> R): R { ... }
因此编译器对writableDatabase 进行类型检查,如下所示(在字节码中):
CHECKCAST java/io/Closeable
所以此时,SQLiteDatabase 的实现必须永远实现Closeable 接口,否则编译代码将失败。通过切换到不再适用的旧版本的 Android,您违反了合同并导致异常。编译器无法做任何不同的事情。这就像我在编译完代码后将任何 JVM 应用程序中的 JAR 换成具有完全不同实现的 JAR 一样。更改 Android 版本基本上会交换所有 JAR。
如果所有版本都有 close 方法(已复制和修改来自 Kotlin 标准库 use 函数):
inline fun <T : SQLiteDatabase, R> T.use(block: (T) -> R): R {
var closed = false
try {
return block(this)
} catch (e: Exception) {
closed = true
try {
close()
} catch (closeException: Exception) {
// eat the closeException as we are already throwing the original cause
// and we don't want to mask the real exception
}
throw e
} finally {
if (!closed) {
close()
}
}
}
现在内联类型检查将是CHECKCAST android/database/sqlite/SQLiteDatabase,它将始终成功。