简体   繁体   English

Kotlin 中的 filter() 和 {} 有什么区别

[英]What is the difference between filter () and {} in Kotlin

I am studying Kotlin and facing some difficulties with understanding the difference between filter with parenthesis and curly braces.我正在学习 Kotlin,并且在理解带括号和花括号的过滤器之间的区别时遇到了一些困难。 If I check filter implementation, Intellij redirects me to the same source.如果我检查过滤器实现,Intellij 会将我重定向到同一个源。

Code example:代码示例:

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())

What If I want to call somehow filter { getSimplePredicate() } ?如果我想以某种方式调用filter { getSimplePredicate() }怎么办?

Is there is a way?有办法吗? And what is the difference?有什么区别? Thanks in advance提前致谢

filter() expects a predicate function as argument, ie a function which takes a string and returns a boolean. filter()需要一个谓词函数作为参数,即一个接受字符串并返回布尔值的函数。

In Kotlin, if the last argument of a function (filter here) is a function (the predicate), then you can pass a lambda and the lambda can be out of the parentheses.在 Kotlin 中,如果函数的最后一个参数(这里是过滤器)是一个函数(谓词),那么你可以传递一个 lambda,并且 lambda 可以在括号之外。 So所以

.filter { ... }

is the same as是相同的

.filter({ ... })

So, in your code, { name -> name.startsWith("j") } is a lambda which is the argument passed to filter() .因此,在您的代码中, { name -> name.startsWith("j") }是一个 lambda,它是传递给filter()的参数。

The function getSimplePredicate() returns a predicate function.函数getSimplePredicate()返回一个谓词函数。

So .filter(getSimplePredicate()) is equivalent to .filter({ name.firstName.startsWith(prefix) }) , which is equivalent to .filter { name.firstName.startsWith(prefix) } .所以.filter(getSimplePredicate())相当于.filter({ name.firstName.startsWith(prefix) }) ,相当于.filter { name.firstName.startsWith(prefix) }

.filter { getSimplePredicate() } would be valid if { getSimplePredicate() } was a function that returns a Boolean, ie if getSimplePredicate() returned a Boolean.如果{ getSimplePredicate() }是一个返回布尔值的函数,即如果getSimplePredicate()返回一个布尔值,则.filter { getSimplePredicate() }将是有效的。 But it doesn't, so it's not valid.但它没有,所以它是无效的。

As JB Nizet mentions, this isn't specific to filtering, but is standard Kotlin syntax.正如 JB Nizet 所提到的,这并非特定于过滤,而是标准的 Kotlin 语法。 If you're calling a function with a lambda as the last parameter, eg:如果您使用 lambda 作为最后一个参数调用函数,例如:

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

Then you can move the lambda outside the parentheses :然后你可以将 lambda 移到括号外

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

(That's mainly to allow functions that look like new language syntax. But it's useful generally.) And if there's nothing left in them, you can omit the parentheses entirely : (这主要是为了允许看起来像新语言语法的函数。但它通常很有用。)如果它们中没有任何东西,你可以完全省略括号

list.map{ size -> size * 2 }

Also, if a lambda has exactly one parameter (and the compiler can infer its type), then you can refer to it as it instead of naming it explicitly:此外,如果 lambda 仅具有一个参数(并且编译器可以推断其类型),那么您可以将其称为it而不是显式命名它:

list.map{ it * 2 }

All four forms mean exactly the same thing : they're calling the map() function with a lambda.所有四种形式的含义完全相同:它们使用 lambda 调用map()函数。 (You'll see those sorts of syntactic shortcuts a lot; they can help to make code easier to read.) (您会经常看到这类语法快捷方式;它们有助于使代码更易于阅读。)

OK, on to your code:好的,进入你的代码:

Your first line works, but your predicate function needs a few tweaks before it'll compile.您的第一行有效,但您的谓词函数在编译之前需要进行一些调整。 The signature is fine, but the definition won't work without braces.签名很好,但如果没有大括号,定义将无法工作。 (In Scala and Java, the arrow is the distinguishing part; but we've just seen how you can omit that in Kotlin, so every lambda must have braces .) (在 Scala 和 Java 中,箭头是显着的部分;但我们刚刚看到了如何在 Kotlin 中省略它,因此每个 lambda 都必须有大括号。)

Also, you haven't defined firstName or prefix .此外,您还没有定义firstNameprefix I'm going to assume that we can ignore the former, and provide the latter as a parameter.我将假设我们可以忽略前者,并将后者作为参数提供。 With a bit of simplification, that gives:稍微简化一下,得出:

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

And with those tweaks, you can indeed use it to provide a predicate for filtering , eg:通过这些调整,您确实可以使用它来提供过滤谓词,例如:

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

(Note that this time, there are no curly braces, as we're not creating a lambda in this line — the function has already done that.) (请注意,这一次没有大括号,因为我们没有在这一行中创建 lambda - 函数已经这样做了。)

Of course, in this case, it's actually simpler to provide a lambda directly, as per your first example!当然,在这种情况下,根据您的第一个示例,直接提供 lambda 实际上更简单! But this illustrates the principle.但这说明了原理。

There's one other option that's worth covering, too, which is a function reference.还有另一个值得一提的选项,它是函数引用。 If you already had a function that would do the job, you don't need to put it in a lambda, but can refer to it directly, using the :: notation.如果您已经有一个可以完成这项工作的函数,则无需将其放入 lambda 中,但可以使用::符号直接引用它。 For example:例如:

fun hasValidPrefix(s: String) = s.startsWith("j")

listOf("john", "dave").filter(::hasValidPrefix)

That only works if the parameter type(s) are compatible, but it's slightly simpler (and can generate slightly more efficient bytecode).这仅在参数类型兼容时才有效,但它稍微简单一些(并且可以生成稍微更有效的字节码)。

All of these are explained in the Kotlin docs .所有这些都在 Kotlin 文档中进行了解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM