I am trying to map a list of objects to a list of objects of another type, then filter the list, then map to a list of a third type, just like I would do by chaining streams in Java 8+ (I changed the class and variable names to make it more meaningful but the structure is the same as in my code):
val results: List<AccountDto> = listOfPersons
.map { person -> getPersonAccount(person) }
.filter { account ->
if(validateAccount(account)){ // validateAccount is a function with boolean return type
// do something here like logging
return true
}
// do something else...
return false
}
.map { account ->
toDto(account) // returns an AccountDto
}
I get a compiler error on the return true
and return false
statements inside the filter lambda :
Error:(217, 32) Kotlin: The boolean literal does not conform to the expected type List<AccountDto>
If I use an anonymous function for the filter predicate it compiles fine :
.filter (fun(account):Boolean{
if(validateAccount(account)){
// do something here like logging
return true
}
// do something else...
return false
})
Why does the type inference fail in that case?
Is it possible to somehow I make it work with just a lambda ?
return
returns from the nearest enclosing function or anonymous function (that's the form fun(a1: T1, ..., an: TN): TR = ...
) . The mnemonic is that an unqualified return
goes up to the nearest fun
. A lambda is not an anonymous function, so the return
is actually returning from the function that contains the whole map
/filter
expression. You can use a labelled return:
val results: List<AccountDto> = listOfPersons
.map(::getPersonAccount)
.filter { account ->
if(validateAccount(account)){
// etc.
return@filter true
}
// etc.
return@filter false
}
.map(::toDto)
A lambda expression is implicitly labelled with the function that it is passed to, so you don't need to add the label explicitly. If you would prefer to have it, you may:
val results: List<AccountDto> = listOfPersons
.map(::getPersonAccount)
.filter lam@{ account ->
if(validateAccount(account)){
// etc.
return@lam true
}
// etc.
return@lam false
}
.map(::toDto)
But an if
/else
is itself an expression, so it suffices to just not have return
s.
val results: List<AccountDto> = listOfPersons
.map(::getPersonAccount)
.filter { account ->
if(validateAccount(account)) {
// etc.
true
} else {
// etc.
false
}
}
.map(::toDto)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With