如何从“使用”调用中“返回”?

时间:2018-04-10 00:12:55

标签: kotlin

在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:A'返回'具有块体的函数中所需的表达式(' {...}')

我找到了解决这个问题的两种方法,但两者都有点尴尬。

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

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在一个不那么尴尬的循环中提前返回?

2 个答案:

答案 0 :(得分:3)

原因Kotlin编译器不够智能,不能理解use内部代码将返回函数中的内容。这种行为的原因是无法保证编译器只能调用一次lambda。

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

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

P.S。我不确定,但是当{3}}被添加到Kotlin时,似乎可以在没有任何黑客的情况下进行编译。它可能会在版本1.3

答案 1 :(得分: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
}