当我在循环内的lambda中返回时为什么Kotlin无法编译

时间:2019-05-04 21:28:39

标签: kotlin lambda

所以我有这个非常简单的代码:

#include <iostream>

int hcf(int n1, int n2);

int main() {

    int n1, n2, n3;

    std::cout << "   Insert 3 numbers: ";
    std::cin >> n1 >> n2 >> n3;

    std::cout << "HCF = " << hcf(n1, hcf(n2, n3));

    return 0;
}

int hcf(int n1, int n2) {

    while (n1 != n2) {

        if (n1 > n2)
            n1 -= n2;

        else
            n2 -= n1;

    }

    return n1;
}

而intellij说:

val a:() -> String = a@ {
    while(true){
            return@a "hello"
    }
}

现在,如果我在函数中有相同的东西

Type mismatch.
Required: String
Found: Unit

完全没问题。

我也不能使用fun b():String { while(true){ return "hello" } }

Callable

相同错误。我可以将其转换为对象清理:

val c : Callable<String> = Callable<String> {
    while(true){
        return@Callable "hello"
    }
}

,并且有效。为什么在使用lambda时不起作用?

2 个答案:

答案 0 :(得分:3)

原因是按照惯例,lambda的last语句是隐式返回,因此您的第一个块被视为类似这样的

function a(): String {
    return while (true) {
        return "hello"
    }
}

基本上可以理解为“返回while块的结果”,并被视为单位。

您可以改用匿名函数绕过此约定:

val a = fun(): String {
    while (true) {
        return "hello"
    }
}

答案 1 :(得分:0)

在具有非Unit返回类型的lambda中,最后一条语句必须为表达式。 while不是表达式,因此Kotlin推断出代码块

while(true){
    return@Callable "hello"
}

旨在返回Unit。编译器不会做更深入的分析来推断这是一个带有非本地return语句的无限循环。

这样,您必须确保lambda的最后一行是类型String的表达式。

我曾经为需要某种 type 但没有值的场景构建了一个辅助函数:

fun <T> declval(): T = throw IllegalStateException("Code should be unreachable")

它是根据C++ std::declval()建模的。因此,您的lambda可能是:

val a:() -> String = a@ {
    while(true){
        return@a "hello"
    }

    declval<String>()
}

但是,如果仅更改无限循环的控制流以产生一个退出点(例如,使用break),可能会更容易理解。