fun OuterFunction(param:Int){
val outerVar = 11
fun localFunction(){
println(params)
println(outerVar)
}
}
Local functions are functions inside a function. Local functions could access outer function's parameters & its local variables (i.e. the closure).
What's the use
This is useful when we want to do code reuse i.e. we don't want to create a top level function or we don't want to create a member function outside of the class.This way grouping is better.
Note For other function outside of outerFunction we don't have access to outerFunction's localFunctions .
for extension functions or member functions that have single parameters we can call these using infix notation
infix fun String.extensionFunction(x: Int): Int {
...
}
//can be called as
val str = "hello"
str extensionFunction 2
useful in UNIT TESTING
We have seen higher-order functions
where lambda expressions are passed as code blocks.
So Anonymous Functions
are slightly different .
fun op(x:Int,op:(Int) -> Int):Int{
return op(x)
}
lambdas
//called like
op(3,{it*it})
//here {it*it} is lambda expression
Anonymous
//can have multiple returns
op(3,fun(x):Int{
if(x>10) return 0
else return x*x
})
Anonymous functions
have full body of a normal function but no name.
A lambda expression in kotlin gives way to anonymous class in java.This adds its overhead and if lambda expression has a closure it would cause an instance to also be created thus adding more memory overhead.
In addition all these lambda expression are impacting the call stack. It affects performance.
Using Inline Functions
we can minimize these impacts.
NonInline
fun op(op:()->Unit){
println("This is before lambda")
op()
println("this is after lambda")
}
fun main(args: Array<String>) {
op({println("this is the actual function")})
}
you can see decompiled kotlin bytecode here noninline
Inline
inline fun op(op:()->Unit){
println("This is before lambda")
op()
println("this is after lambda")
}
fun main(args: Array<String>) {
op({println("this is the actual function")})
}
you can see decompiled kotlin bytecode here inline
if you compare the two decompiled versions you can see that what inline modifier does is
copy the entire inline function code to where we are invoking that function and it inline(copy) the code of the lambda being passed too
Inline takes a higher order function and inline it(copy and paste contents of that function) being passed in to where it is being called.
So, essentially it's flattening it out, it's saying instead of me invoking this function I'm gonna take the code and I'm gonna paste it there.
And this provides us with optimization because we are eliminating all of that unnecessary anonymous classes or call stack etc.
Of course, it comes with it's own side effects since we are copying and pasting code. So inline modifier is obviously useful when I'm passing in the lambdas because otherwise it makes no sense.
Note we can't actually store reference to that lambda function if want to inline it.
inline fun op(op:()->Unit){
val reference = op //this woule say illegal usage
op()
println("this is after lambda")
}
fun main(args: Array<String>) {
op({println("this is the actual function")})
}
If you more than one lambdas passed in as parameters to higher order functions you can specifically tell the compiler to not inline specific lambda function by using noinline
modifier in front.
If body of function is large you may don't wanna inline it.
Let's take an example
fun ContainingFunction(){
val nums=1..100
numbers.forEach{
if(it%5==0){
return
}
}
println("Hello!")
}
If you run this you would get an empty output i.e. no Hello!
printed.
Because the input parameter to forEach function is a lambda expression and
return statement make us return from the containingFuction itself rather than the lambda expression.
So how to retun from lambda rather than containing function
Seems kotln has a way i.e. change the return statement
labels
...
if(it%5==0){
return@forEach //i.e. name of the higher order function
}
...
//or you could define your own labels
numbers.forEach myLabel@{
if(it%5==0){
return@myLabel
}
....
Note But instead of lambda expression if we use anonymous function as parameter it would return from the anounymous function itself without any labels
Also non local returns are only allowed in the case where they are invoked from an inline function. what i means is if we look kotlin's forEach code you can see that it has inline modifer in it
So if we remove inline modifier from forEach it would show error.
or lambda receivers
//lambdas
f:() -> Unit
//lambda extension
f:SomeFunctionOrClass.() -> Unit
Take time to look at this sample resthandler
we can see that routeHandler has lambda extension as parameter
fun routeHandler(path:String,f:RouteHandler.() -> Unit):RouteHandler.() -> Unit = f
So all extension functions have access to members of the class they are extending i.e. routeHandler has excess to the request and response members of the the RouteHandler class
This Helps us in creating a very fluent ,expressive DSLs further example
or lambda receivers
... main{
...
val manager = Manager()
...
}
class Manager{
}
Now Imagine a scenario in which I want to invoke some functionality of Manager class Just by using the instance like
manager("do Something")
We can do that with kotlin
We can simply implement a function called invoke which is defined as an operator and which can take any parameter
class Manager{
operator fun invoke(value:String){
prinln(value)
}
}