从 finally 块返回

Returning from finally block

我有 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
    }
}

按我的预期工作并且总是 returns 3.

没想到,当我使用隐式 return 时,行为是不同的

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

并且 3 从未 returned。
为什么这两个函数的工作方式不同?

documentation here里面说当try/catch用到一个表达式:

The contents of the finally block don't affect the result of the expression.

在第一个示例中,即使您的 finally 块不影响表达式的计算,它也是 short-circuiting 完全由 return 直接调用的表达式。

在第二个示例中,finally 没有做任何事情,因为它不影响表达式,所以忽略该值。

在实践中,如果您 return 从finally 块,因为它是无用的代码。

与 Java 中的行为不同是因为 try ... catch ... finally 在 Kotlin 中是一个 表达式 ,而不是一个语句。

评估“try-expression”的方式在 specification 中定义如下:

The try-expression evaluation evaluates its body; if any statement in the try body throws an exception (of type E), this exception, rather than being immediately propagated up the call stack, is checked for a matching catch block. If a catch block of this try-expression has an exception parameter of type T:>E , this catch block is evaluated immediately after the exception is thrown and the exception itself is passed inside the catch block as the corresponding parameter. [...]

If there is a finally block, it is evaluated after the evaluation of all previous try-expression blocks

The value of the try-expression is the same as the value of the last expression of the try body (if no exception was thrown) or the value of the last expression of the matching catch block (if an exception was thrown and matched). All other situations mean that an exception is going to be propagated up the call stack, and the value of the try-expression is undefined.

Note: as described, the finally block (if present) is always executed, but has no effect on the value of the try-expression.

所以当throwing为真时,trycatchfinally块都执行,但是try-expression 的 value 是 catch 块中最后一个表达式的值。这解释了“显式”和“隐式”情况下的行为。

在“explicit return”的情况下,return 2 被执行,但是方法不能 return - finally 块仍然必须 运行!然后return 3被执行,现在方法returns。值得注意的是,外部 return 永远不会被执行。您可以删除外部 return 并使用 try { ... 启动方法并获得相同的结果。

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

旁注:return ... 也是表达式,它们的类型为 Nothing(每种类型的子类型),因为它们从不计算 任何事物。这就是为什么您可以在“显式 return”情况下将 return 1 等写为 try-expression 块的最后一行。在那种情况下,try-expression 实际上具有 Nothing 的类型,而外部 return 实际上是 returning Nothing。这很好,因为 NothingInt.

的子类型

您可以在 kotlin

中执行类似的操作
fun tryExpExplicit(throwing: Boolean): Int {
    return runCatching {
        // do something
    }.onFailure { exception ->
        // similar to catch
    }.onSuccess { data ->
        // similar to finally
    }.getOrThrow() // get the data or throw
}

内部 runCatching 使用 try/catch 与 java 类似的块。你可以想到

  • runCatching 作为 try
  • .onFailure 作为 catch
  • .onSuccess 作为 finally

您还可以使用它对 return 值

的扩展
  • .getOrThrow如果要return数据或抛出异常
  • .getOrNull 如果你想 return 数据或者 null 如果它抛出异常
  • .getOrElse 如果你想要 return 数据,否则如果它抛出异常
  • .getOrDefault 如果你想 return 数据或者默认如果它抛出异常