为什么在Kotlin中链接.map()和.filter()不能按预期工作?

Pie*_*nry 4 functional-programming kotlin java-stream

我试图将对象列表映射到另一种类型的对象列表,然后过滤列表,然后映射到第三种类型的列表,就像我在Java 8+中链接流一样(我更改了类和变量名称,使其更有意义,但结构与我的代码相同):

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
                }
Run Code Online (Sandbox Code Playgroud)

我在过滤器lambda中的return true和return false语句上遇到编译器错误:

Error:(217, 32) Kotlin: The boolean literal does not conform to the expected type List<AccountDto>
Run Code Online (Sandbox Code Playgroud)

如果我对过滤谓词使用匿名函数,它编译很好:

.filter (fun(account):Boolean{
                if(validateAccount(account)){ 
                        // do something here like logging
                        return true
                    }
                    // do something else...
                    return false
            })
Run Code Online (Sandbox Code Playgroud)

为什么类型推断在这种情况下失败?

有可能以某种方式让它只用一个lambda吗?

HTN*_*TNW 11

return从最近的封闭函数或匿名函数(即表单fun(a1: T1, ..., an: TN): TR = ...)返回.助记符是不合格return的上升到最近的fun.lambda不是匿名函数,因此return它实际上是从包含整个map/ filterexpression 的函数返回的.您可以使用带标签的退货:

val results: List<AccountDto> = listOfPersons
                .map(::getPersonAccount)
                .filter { account ->
                    if(validateAccount(account)){
                        // etc.
                        return@filter true
                    }
                    // etc.
                    return@filter false
                }
                .map(::toDto)
Run Code Online (Sandbox Code Playgroud)

lambda表达式使用传递给它的函数隐式标记,因此您不需要显式添加标签.如果您希望拥有它,您可以:

val results: List<AccountDto> = listOfPersons
                .map(::getPersonAccount)
                .filter lam@{ account ->
                    if(validateAccount(account)){
                        // etc.
                        return@lam true
                    }
                    // etc.
                    return@lam false
                }
                .map(::toDto)
Run Code Online (Sandbox Code Playgroud)

但是if/ else它本身就是一个表达式,所以只需要没有returns 就足够了.

val results: List<AccountDto> = listOfPersons
                .map(::getPersonAccount)
                .filter { account ->
                    if(validateAccount(account)) {
                        // etc.
                        true
                    } else {
                        // etc.
                        false
                    }
                }
                .map(::toDto)
Run Code Online (Sandbox Code Playgroud)