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