【问题标题】:What is the difference between filter () and {} in KotlinKotlin 中的 filter() 和 {} 有什么区别
【发布时间】:2019-12-02 20:12:25
【问题描述】:

我正在研究 Kotlin,在理解带括号的过滤器和花括号之间的区别时遇到了一些困难。如果我检查过滤器实现,Intellij 会将我重定向到同一来源。

代码示例:

listOf("john", "dave").filter { name -> name.startsWith("j") }

// versus

// simple predicate function
fun getSimplePredicate(): (String) -> Boolean = 
  name: String -> name.firstName.startsWith(prefix)

// actual filter call
listOf("john", "dave).filter(getSimplePredicate())

如果我想以某种方式调用filter { getSimplePredicate() } 怎么办?

有办法吗?有什么区别?提前致谢

【问题讨论】:

    标签: kotlin lambda filter predicate


    【解决方案1】:

    filter() 需要一个谓词函数作为参数,即一个接受字符串并返回布尔值的函数。

    在 Kotlin 中,如果函数(此处为过滤器)的最后一个参数是函数(谓词),那么您可以传递一个 lambda,并且 lambda 可以不在括号内。所以

    .filter { ... }
    

    一样
    .filter({ ... })
    

    因此,在您的代码中,{ name -> name.startsWith("j") } 是一个 lambda,它是传递给 filter() 的参数。

    函数getSimplePredicate() 返回一个谓词函数。

    所以.filter(getSimplePredicate())等价于.filter({ name.firstName.startsWith(prefix) }),也就是等价于.filter { name.firstName.startsWith(prefix) }

    如果{ getSimplePredicate() } 是一个返回布尔值的函数,即如果getSimplePredicate() 返回一个布尔值,

    .filter { getSimplePredicate() } 将是有效的。但它没有,所以它无效。

    【讨论】:

    • 感谢您的精彩解释,现在对我来说完全有意义
    【解决方案2】:

    正如 JB Nizet 所提到的,这不是特定于过滤的,而是标准的 Kotlin 语法。如果你调用一个以 lambda 作为最后一个参数的函数,例如:

    list.map({ size -> size * 2 })
    

    然后你可以把lambda移到括号外

    list.map(){ size -> size * 2 }
    

    (这主要是为了允许看起来像新语言语法的函数。但它通常很有用。)并且如果其中没有任何内容,您可以完全省略括号

    list.map{ size -> size * 2 }
    

    另外,如果一个 lambda 表达式只有一个参数(并且编译器可以推断其类型),那么您可以将其称为 it 而不是显式命名:

    list.map{ it * 2 }
    

    所有四种形式的含义完全相同:它们使用 lambda 调用 map() 函数。 (您会经常看到这类语法快捷方式;它们有助于使代码更易于阅读。)

    好的,进入你的代码:

    您的第一行有效,但您的谓词函数需要一些调整才能编译。签名很好,但如果没有大括号,定义将无法工作。 (在 Scala 和 Java 中,箭头是区分的部分;但我们刚刚看到如何在 Kotlin 中省略它,因此每个 lambda 都必须有大括号。)

    另外,您还没有定义firstNameprefix。我将假设我们可以忽略前者,并将后者作为参数提供。稍微简化一下,得到:

    fun getSimplePredicate(prefix: String): (String) -> Boolean
        = { it.startsWith(prefix) }
    

    并且通过这些调整,您确实可以使用它来提供用于过滤的谓词,例如:

    listOf("john", "dave").filter(getSimplePredicate("j"))
    

    (请注意,这次没有花括号,因为我们没有在这一行中创建 lambda - 函数已经完成了。)

    当然,在这种情况下,根据您的第一个示例,直接提供 lambda 实际上更简单!但这说明了原理。

    还有另一个值得一提的选项,即函数引用。如果您已经有一个可以完成这项工作的函数,则不需要将它放在 lambda 中,但可以使用 :: 表示法直接引用它。例如:

    fun hasValidPrefix(s: String) = s.startsWith("j")
    
    listOf("john", "dave").filter(::hasValidPrefix)
    

    这只有在参数类型兼容时才有效,但它稍微简单一些(并且可以生成更高效的字节码)。

    所有这些都解释了in the Kotlin docs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-24
      • 2018-12-22
      • 2012-01-14
      • 2011-10-17
      • 2019-08-19
      • 2013-03-22
      • 1970-01-01
      • 2020-07-07
      相关资源
      最近更新 更多