Почему kotlin не компилируется, когда у меня есть возврат внутри лямбды внутри цикла - PullRequest
0 голосов
/ 05 мая 2019

Итак, у меня есть очень простой код:

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

И Intellij говорит:

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

Та же ошибка.Я мог бы преобразовать его в декларацию объекта:

val d :Callable<String> = object :Callable<String>{
    override fun call(): String {
        while(true)
            return "hello"
    }
}

, и это работает.Почему не работает при использовании лямбды?

Ответы [ 2 ]

3 голосов
/ 05 мая 2019

Причина в том, что по условию для лямбда-выражения последний оператор является неявным возвратом, поэтому ваш первый блок обрабатывается как примерно так :

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

Что в принципе можно прочитать как«вернуть результат блока while», и он считается единицей.

Вы можете использовать анонимную функцию, чтобы обойти это соглашение:

val a = fun(): String {
    while (true) {
        return "hello"
    }
}
0 голосов
/ 05 мая 2019

В лямбда-выражении с типом, отличным от Unit, последний оператор должен быть выражением. while не является выражением, и поэтому Котлин делает вывод, что кодовый блок

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

предназначен для возврата Unit. Компилятор не делает более глубокий анализ, чтобы сделать вывод, что это бесконечный цикл с нелокальным оператором возврата.

Таким образом, вы должны убедиться, что последняя строка вашей лямбды является выражением типа String.

Однажды я создал вспомогательную функцию для сценариев, где ожидается определенный тип , но не значение:

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

Он моделируется после C ++ std :: declval () . Итак, ваша лямбда может быть:

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

    declval<String>()
}

Но это может быть легче понять, если вы просто измените поток управления вашего бесконечного цикла, чтобы получить одну точку выхода (например, с помощью break).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...