【问题标题】:Why does chaining .map() and .filter() in Kotlin not work as expected?为什么在 Kotlin 中链接 .map() 和 .filter() 不能按预期工作?
【发布时间】:2018-12-16 13:46:38
【问题描述】:

我正在尝试将对象列表映射到另一种类型的对象列表,然后过滤列表,然后映射到第三种类型的列表,就像我在 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
                }

过滤器 lambda 中的 return truereturn false 语句出现编译器错误:

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

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

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

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

是否有可能以某种方式使其仅使用 lambda 工作?

【问题讨论】:

    标签: functional-programming kotlin java-stream


    【解决方案1】:

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

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

    一个 lambda 表达式被它传递给的函数隐式标记,所以你不需要显式添加标签。如果您想拥有它,您可以:

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

    但是if/else 本身就是一个表达式,所以没有returns 就足够了。

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

    【讨论】:

    • 不错!感谢您提供完整的示例!我将接受的答案更改为这个答案,因为它更完整。
    【解决方案2】:

    您会收到该错误,因为 lambda 中的普通 return 倾向于从最外层范围(即函数本身)返回。

    Lambda 期望隐式返回(每个执行路径上的最后一个表达式)或显式标记返回 return@filter

    删除returns 或用return@filter 替换它们将修复错误。

    【讨论】:

    • 谢谢,我不知道。如您所见,我是 Kotlin 菜鸟 ;)
    猜你喜欢
    • 2020-02-22
    • 2018-04-09
    • 1970-01-01
    • 2021-05-30
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多