A parameter that looks like this:
someParameter: Something -> SomethingElse
is a function type. Just like how you can pass in parameters like String
s or Int
s, you can pass around functions too. The notation tells you what parameters the function takes, and what its return value is.
(String) -> Unit
represents a function that has a single String
parameter, and returns Unit
(i.e. nothing). (Int, Int) -> Int
represents a function that takes two Int
parameters and returns a single Int
. You can probably think of a few functions that match that!
Assuming you already know what an extension function is (since you said this parameter was the bit you were confused about), then T.() -> Unit
just represents a function you call on a receiver of type T
, which takes no parameters, and returns Unit
. It basically means you can do this:
"hi I'm a String".apply({ println(this) })
// or more usually, moving the lambda outside of the parentheses:
"hi I'm a String".apply { println(this) }
The lambda's type is T.() -> Unit
- because you're calling apply
on a String
, that means T
in this case is String
. So your lambda's receiver, this
in its scope, is a String
- and you can make use of that in the lambda. It's usually used to configure something, e.g.
val myThing = SomeObject().apply {
setOption(true)
addMessage("Hi")
}
instead of
val myThing = SomeObject()
myThing.setOption(true)
myThing.addMessage("Hi")
See how the object can be excluded in the apply
since it's the receiver, and you don't need to explicitly say this
?
There are a bunch of these scope functions, like apply
, run
, with
that all do something different in terms of what they return. apply
returns the original object, not the result of the lambda - see how the function T.() -> Unit
returns Unit
, meaning any value you express will be ignored by apply
(which returns the original object you called it on). run
on the other hand, returns the result of the lambda, which could be a different type.
There are also equivalents that take a parameter
instead of a receiver
- e.g. also
is the parameter equivalent of apply
:
fun <T> T.apply(block: T.() -> Unit): T
fun <T> T.also(block: (T) -> Unit): T
See how that works? Same deal, but if you use that with a String
, it's accessible in the function as a parameter instead (named it
by default for a single parameter). Which you use depends on what's appropriate for the situation, and whether you think a named parameter is more readable or not