Reputation: 539
So I have this very simple code:
val a:() -> String = a@ {
while(true){
return@a "hello"
}
}
And intellij says:
Type mismatch.
Required: String
Found: Unit
Now, if I had the same thing, like, in a function
fun b():String {
while(true){
return "hello"
}
}
It's totally fine.
I also can't use a Callable
val c : Callable<String> = Callable<String> {
while(true){
return@Callable "hello"
}
}
Same error. I could convert it to an object decleration:
val d :Callable<String> = object :Callable<String>{
override fun call(): String {
while(true)
return "hello"
}
}
and that works. Why doesn't it work when using a lambda?
Upvotes: 1
Views: 556
Reputation: 6353
The reason is that by convention for lambda last statement is an implicit return, so your first block is treated as something like this:
function a(): String {
return while (true) {
return "hello"
}
}
Which basically can be read as "return the result of while block" and it is considered as Unit.
You can use anonymous function instead to bypass this convention:
val a = fun(): String {
while (true) {
return "hello"
}
}
Upvotes: 4
Reputation: 6526
In a lambda that has a non-Unit
return type, the last statement must be an expression. while
is not an expression, and thus Kotlin infers that the code block
while(true){
return@Callable "hello"
}
is meant to return Unit
. The compiler does not do deeper analysis to infer that it's an infinite loop with a non-local return statement.
As such, you must make sure that the last line of your lambda is an expression of type String
.
I once built a helper function for scenarios where a certain type is expected, but not a value:
fun <T> declval(): T = throw IllegalStateException("Code should be unreachable")
It is modeled after C++ std::declval(). So, your lambda could be:
val a:() -> String = a@ {
while(true){
return@a "hello"
}
declval<String>()
}
But it might be easier to understand if you just change the control flow of your infinite loop to result in one exit point (e.g. using break
).
Upvotes: 1