【问题标题】:How can I `return` from inside of a call to `use`?如何从调用“使用”中“返回”?
【发布时间】:2018-04-10 00:12:54
【问题描述】:

在 Kotlin 中,这段代码可以编译:

private fun bar(): Boolean = TODO()

fun works(): Int {
    while (true) {
        if (bar()) {
            return 5
        }
    }
}

(这是我真实代码的精简示例,用于说明我遇到的问题。)

我实际上需要在这个循环中使用一个文件,并在退出时关闭:

fun openFile(): InputStream = TODO()

fun doesnt_work(): Int {
    openFile().use { input ->
        while (true) {
            if (bar()) {
                return 5
            }
        }
    }
 } // line 42

这不会编译。我得到错误:

错误:(42, 5) Kotlin: 带有块体 ('{...}') 的函数中需要一个 'return' 表达式

我找到了两种解决此问题的方法,但都有些尴尬。

一种方法是使用变量来保存结果,并在设置后立即从循环中中断:

fun works_but_awkward(): Int {
    openFile().use { input ->
        val result: Int
        while (true) {
            if (bar()) {
                result = 5
                break
            }
        }
        return result
    }
}

这在我的真实代码中特别尴尬,因为我有一个嵌套循环,所以我需要使用带标签的中断。

解决此问题的另一种方法是为循环指定一个命名函数:

fun workaround_with_named_function(): Int {
    fun loop(input: InputStream): Int {
        while (true) {
            if (bar()) {
                return 5
            }
        }
    }
    return openFile().use { loop(it) }
}

这似乎好一点,但我仍然感到惊讶的是,use 抽象是如此泄漏,以至于我无法从循环中提前返回。有没有办法在不那么尴尬的循环中使用use 并提前返回?

【问题讨论】:

  • 如果您在 use 块之后返回一个虚拟值,编译器会在您的第一个 doesnt_work() 示例中感到满意,即使它永远不会返回。我建议在discuss.kotlinlang.org 提出这个问题,以便从事该语言/图书馆工作的人们希望看到它或提供更好的答案。
  • 请看看我的回答,我认为它看起来比你的解决方法和其他答案都好。

标签: kotlin


【解决方案1】:

因为 Kotlin 编译器不够聪明,无法理解带有代码的 use 会从函数返回一些内容。出现这种行为的原因是编译器无法保证 lambda 只会被调用一次。

解决此问题的另一种方法是在函数末尾抛出异常:

fun doesnt_work(): Int {
    openFile().use { input ->
        while (true) {
            if (bar()) {
                return 5
            }
        }
    }
    throw IllegalStateException("Something goes wrong")
}

P.S.我不确定,但是当contract system 被添加到 Kotlin 时,它似乎可以在没有任何 hack 的情况下编译。它可能会出现在 1.3 版中

【讨论】:

  • 我认为AssertionError 是正确的,因为它不应该发生。
【解决方案2】:

这应该可行。

fun openFile(): InputStream = TODO()

fun doesnt_work(): Int {
    return openFile().use { input ->
        while (true) {
            if (bar()) {
                return@use 5
            }
        }
        -1 // unreachable return value
           // just to help Kotlin infer the return type
    }
 }

请记住,use 是一个函数,其返回值与 lambda 的返回值完全相同。所以在 lambda 中返回值(这里是 5)并返回 use 的返回值应该可以工作。

另外,如果我是你,我会这样写函数:

fun doesnt_work() = openFile().use { input ->
    while (true) if (bar()) return@use 5
    -1
}

【讨论】:

  • 这对我不起作用。我在use 上得到Type inference failed. Expected type mismatch: inferred type is Any but Int was expected,在while 上得到Type mismatch: inferred type is Unit but Int was expected。我正在使用 kotlinc-jvm 1.2.31。
  • 添加无法访问的最终返回语句将起作用,请参阅我的更新。抱歉没有测试代码
  • @LaurenceGonsalves 嗨,你能看看我的编辑吗?
猜你喜欢
  • 1970-01-01
  • 2017-09-05
  • 1970-01-01
  • 1970-01-01
  • 2018-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多