从finally块返回

Tom*_*ekK 2 return try-catch kotlin

我有Java背景,最近开始学习Kotlin。现在我正在读一本书“Programming Kotlin”,我看到了一个带有 try-catch 表达式的片段。这促使我编写了一些简单的函数来比较它在 Java 和 Kotlin 中的工作方式。第一个功能:

fun tryExpExplicit(throwing: Boolean): Int {
    return try {
        if (throwing) {
            throw RuntimeException()
        }
        return 1
    } catch (e: Exception) {
        return 2
    } finally {
        return 3
    }
}
Run Code Online (Sandbox Code Playgroud)

按我的预期工作并且总是返回 3。

出乎意料的是,当我使用隐式返回时,行为不同

fun tryExpImplicit(throwing: Boolean): Int {
    return try {
        if (throwing) {
            throw RuntimeException()
        }
        1
    } catch (e: Exception) {
        2
    } finally {
        3
    }
}
Run Code Online (Sandbox Code Playgroud)

并且 3 永远不会返回。
为什么这两个函数的工作方式不同?

Swe*_*per 5

与 Java 中行为的区别是因为Kotlin 中try ... catch ... finally表达式,而不是语句。

规范中对“try-表达式”求值的方式定义如下:

try 表达式评估评估其主体;如果 try 主体中的任何语句抛出异常(类型 E),则该异常不会立即在调用堆栈中传播,而是检查是否有匹配的 catch 块。如果此 try 表达式的 catch 块具有 T:>E 类型的异常参数,则抛出异常后立即计算此 catch 块,并将异常本身作为相应的参数传递到 catch 块内。[...]

如果存在finally块,则在所有先前的try表达式块的求值之后对其进行求值

try 表达式的值与 try 主体的最后一个表达式的值相同(如果没有抛出异常)或匹配的 catch 块的最后一个表达式的值(如果抛出异常并匹配)。所有其他情况都意味着异常将在调用堆栈中向上传播,并且 try 表达式的值未定义。

注意:如上所述,finally 块(如果存在)始终会执行,但对 try 表达式的值没有影响。

因此,当为throwingtrue 时,trycatch、 和finally块都会被执行,但try 表达式的值是 catch 块中最后一个表达式的值。这解释了“显式”和“隐式”情况下的行为。

在“显式返回”的情况下,return 2会执行,但该方法无法返回那里——finally 块仍然必须运行!然后return 3执行,现在方法返回。值得注意的是,外部return永远不会被执行。您可以删除外部return并启动该方法try { ...并获得相同的结果。

在“隐式返回”的情况下,2被评估,并且不会发生副作用,因为它只是一个文字。然后finally块运行并被3评估,并且再次没有副作用发生,因为它只是一个文字。现在我们已经完成了整个try- 表达式的求值,根据规范,表达式的值应该是我们在 catch 块中求值的值 - 即 2。现在我们执行return 2

旁注:也是表达式,并且它们具有(每种类型的子类型)return ...的类型,因为它们从不评估任何内容。这就是为什么您可以在“显式返回”情况下将 etc 写为 try 表达式块中的最后一行。在这种情况下, try 表达式实际上具有 类型,并且外部实际上返回。这很好,因为是 的子类型。Nothingreturn 1NothingreturnNothingNothingInt