首页 文章

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

提问于
浏览
2

在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 与循环中的早期返回不那么尴尬?

2 回答

  • 3

    原因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 . 它可能会在版本1.3中

  • 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
    }
    

相关问题