Lambdas
function types
Kotlin support function types. Values of that types can be anonymous functions, lambdas or reference to the class method.
To declare function type you list types of parameters in parentheses and after arrow specify return type like (A, B) -> C.
The parameter types list may be empty, as in () -> A.
Use Unit type when no return value like (A, B) -> Unit.
Optionally, parameters can be named: (x: Int, y: Int) -> Point.
// create alias
typealias ClickHandler = (Button, ClickEvent) -> Unit
// assign reference to the method of current class
var clickHandler: (Button, ClickEvent) -> Unit = ::onClick;
// assign reference to the method of object
var clickHandler: (Button, ClickEvent) -> Unit = view::onClick;
// assign anonimous function
var clickHandler: (Button, ClickEvent) -> Unit = fun (bt: Button, event: ClickEvent){
// do something
}
// assign lambda expression
val l : (Int, String) -> String = {a: Int, s: String ->
val b = a*a
s+" "+b
}
// in lambda types of parameters can be omitted
val l2 : (Int, String) -> String = {a, s ->
val b = a*a
s+" "+b
}
lambda expression
Lambdas are always declared in curly braces. At begin you specify list of parameters, then the arrow then the body of lambda.
It is allowed not to declare the only one parameter and omit ->. In this case the parameter will be implicitly declared under the name it.
You can explicitly return a value from the lambda using the qualified return syntax. Otherwise, the value of the last expression is implicitly returned, unless the return type is Unit.
// filter method accept (it: Int) -> Boolean argument
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
higher-order function
A higher-order function is a function that takes functions as parameters, or returns a function.
You can call lambdas via the invoke() method or operator ()
fun filter(f : (it: Int) -> Boolean){
// .. do something
val b = f.invoke(34)
val c = f(34)
}
// calling filter function
filter({it -> it%2==0 }) // with default name "it"
filter({ anotherName -> // with custom name "anotherName"
anotherName%2==0
})
Last lambda argument can be placed outside the parentheses. Such syntax is also known as trailing lambda.
// calling filter function with trailing lambda
filter(){
it%2==0
}
If the lambda is the only argument to that call, the parentheses can be omitted entirely.
filter { it%2==0 }
closure
A lambda expression / anonymous function / local function / object expression can access its closure, which includes the variables declared in the outer scope. The variables captured in the closure can be modified.
inline
The inline modifier allows to inline the function itself and the lambdas passed to it into the call site. It is useful because closures cause memory allocations and virtual calls introduce runtime overhead.
// lock is inline function
lock(l) { foo() }
// this call will generate following code
l.lock()
try {
foo()
} finally {
l.unlock()
}
noinline allows you to specify which lambda argument should not be inline.
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }
Inlining may cause the generated code to grow; however, if you do it in a reasonable way (avoiding inlining large functions), it will pay off in performance, especially at "megamorphic" call-sites inside loops.
return value
The final expression is the value that will be returned after a lambda is executed.
In Kotlin, you can only use a normal, unqualified return to exit a named function or an anonymous function. To exit a lambda, use a label.
fun foo() {
ordinaryFunction {
return@foo
}
}
fun foo() {
ordinaryFunction {
return
// ERROR cannot make
// `foo` return here
}
}
But if the function the lambda is passed to is inlined, the return can be inlined as well. Such returns (located in a lambda, but exiting the enclosing function) are called non-local returns. This sort of construct in loops, which inline functions often enclose:
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // returns from hasZeros
}
return false
}
functional interface
You can declare a single abstract method (SAM) interface as a functional interface. This allows you to convert the interface to a lambda and vice versa.