I am studying Kotlin and facing some difficulties with understanding the difference between filter with parenthesis and curly braces. If I check filter implementation, Intellij redirects me to the same source.
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() }
?
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.
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. 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()
.
The function getSimplePredicate()
returns a predicate function.
So .filter(getSimplePredicate())
is equivalent to .filter({ name.firstName.startsWith(prefix) })
, which is equivalent to .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. But it doesn't, so it's not valid.
As JB Nizet mentions, this isn't specific to filtering, but is standard Kotlin syntax. If you're calling a function with a lambda as the last parameter, eg:
list.map({ size -> size * 2 })
Then you can move the lambda outside the parentheses :
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:
list.map{ it * 2 }
All four forms mean exactly the same thing : they're calling the map()
function with a lambda. (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 .)
Also, you haven't defined firstName
or prefix
. 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.)
Of course, in this case, it's actually simpler to provide a lambda directly, as per your first example! 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. 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 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.