Higher-order functions, anonymous functions, and lambda expressions
Kotlin functions are first-class, which means they can be stored in variables and data structures, passed as arguments to other higher-order functions, and returned from other higher-order functions. Functions can be manipulated just like any other non-function value.
First-class function: A first-class function is a function that is treated as a first-class citizen in a programming language. This means that functions can be assigned to variables or stored in data structures as arguments to other functions, as return values of functions.
Higher-order functions
Higher-order functions are functions that use functions as arguments or return values.
//learnHighFun is a higher-order function because it takes a function type argument, funParam. Note that there is a new noun, function type. Functions are also a type in Kotlin. What kind of function is it? Note that (Int) ->Int is a function that takes an Int and returns an argument of type Int.
fun learnHighFun(funParam:(Int) - >Int,param:Int){}
Copy the code
So that’s the simplest higher-order function. Before we get to higher-order functions, it’s obviously worth looking at the new term above, function types
Function types
How do I declare parameters of a function type
In Kotlin, it’s very easy to declare the format of a function type. In Kotlin, we use the -> symbol to organize the parameter type and the return value type. The left and right sides are the parameters of the function, and the right side is the return value of the function. As follows:
// represents the function type, takes an argument of type Int and returns a value of type Int
(Int) - >Int
// Represents the function type. It takes two arguments, one Int and one String, and returns an Int
(Int,Stirng)->Int
Copy the code
How do you declare a function with no function parameters and no return value? As follows:
// Declare a function type that takes no arguments and returns an Int. () cannot be omitted if the function takes no arguments() - >Int
// Specify a function type that has no arguments and no return value. In function types, Unit cannot be omitted when the function has no return value() - >Unit
Copy the code
So that’s the simple declaration of function types. So if it’s a higher-order function, and its argument type is also a higher-order function, how do you declare it? For example, what does this mean?
private fun learnHigh(funParams:((Int) - >Int) - >Int){}
// This is a higher-order function, learnHigh, which takes a function type argument, funParams. The funParams type is also the type of a higher-order function. FunParams is a function type that takes an argument of a normal function class (Int)->Int and returns an Int. This is really confusing to read, but once you understand this complicated example, you should be able to make sense of almost all higher-order functions.
// The highParam type matches the type of the learnHigh function received above
fun highParam(param: (Int) - >Int):Int{
return 1
}
Copy the code
Higher-order functions whose arguments are function types and whose return values are function types are basically the same as above. So the next question is, I’ve talked about so many higher-order functions, so many types of functions. So how do you pass the parameters of these function types? In other words, how do you pass the arguments of these function types to higher-order functions? Can I just use the function name? Obviously not, because the function name is not an expression and has no type information. Then we need a pure method reference expression.
Function reference
In Kotlin, two colons are used to implement a reference to a method of a class. What information does this sentence contain? First, since it is a reference, the specification is an object. That is, a reference implemented with a double colon is also an object. It is an object of type function. Second, since the object needs to be created, that is, an object of type function is created. This object has the same function as the function. Here are some examples of what these two sentences mean:
fun testFunReference(a){
funReference(1) // A normal function, called directly by the function name followed by arguments.
val funObject = ::funReference // A reference to a function that is essentially an object
testHighFun(funObject) // Pass an object of this type to a higher-order function via a function reference. So the arguments received in higher-order functions are essentially objects.
funObject.invoke(1) // equivalent to funReference(1)
funObject(1) // Invoke funReference(1), invoke funobject.invoke (1)
}
fun funReference(param:Int){
//doSomeThing
}
fun testHighFun(funParam:(Int) - >Unit){
//doSomeThing
}
Copy the code
// This is the decompiled Java code
public final void testFunReference(a) {
this.funReference(1);
//val funObject = ::funReference
KFunction funObject = new Function1((TestFun)this) {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke(((Number)var1).intValue());
return Unit.INSTANCE;
}
public final void invoke(int p1) {
((TestFun)this.receiver).funReference(p1); }};this.testHighFun((Function1)funObject);
((Function1)funObject).invoke(1);
((Function1)funObject).invoke(1);//funObject(1) finally calls funobject.invoke (1)
}
public final void funReference(int param) {}// testHighFun receives an object of type Function1
public final void testHighFun(@NotNull Function1 funParam) {
Intrinsics.checkNotNullParameter(funParam, "funParam");
}
Copy the code
That’s it for function references.
I understand the above usage, but it seems to require a function to be declared each time. Are there other ways to call higher-order functions that do not require a function to be redeclared? Well, it’s definitely there, and if it doesn’t support Kotlin’s higher-order function, isn’t that a little bit of a bug? Let’s move on to the other two, anonymous functions in Kotlin and Lambda expressions.
Anonymous functions
Let’s talk about an anonymous function, which by definition is a function without a name, and notice that the word ‘function’ is in quotes. Let’s first see how it works in higher-order functions.
// Continue with the example above
// Instead of calling testHighFun(funObject) via a reference object, you can take a function directly as an argument to this higher-order function.
val param = fun (param:Int){ // Note that there is no function name, so it is anonymous' function '.
//doSomeThing
}
testHighFun(param)
Copy the code
Note: The function testHighFun receives a reference to a function object, meaning that val Param is a reference to a function object. The anonymous’ function ‘fun(param:Int){}, Its essence is a function object. It’s not a function. We can take a look at the decompiled Java code
Param is a reference to an object of type Function1
Function1 param = (Function1)null.INSTANCE;
this.testHighFun(param);
Copy the code
So remember, Kotlin’s anonymous function, it’s not a function by nature. It’s the object. It’s not the same thing as a function, it’s an object of type function. Objects and functions, they’re two different things.
Lambda expressions
The full syntax of a Lambda expression is as follows:
val sum: (Int.Int) - >Int = { x: Int, y: Int -> x + y }
Copy the code
Lambda expressions are always enclosed in curly braces, full syntactic parameter declarations are enclosed in curly braces, with optional type annotations, and the function body is followed by a -> symbol. If the inferred return type of the Lambda is not Unit, then the last (or possibly single) expression in the body of the Lambda is treated as the return value.
Since Kotlin supports type push to, this can be simplified into two formats:
val sum= { x: Int, y: Int -> x + y }
Copy the code
val sum: (Int.Int) - >Int = { x, y -> x + y }
Copy the code
It is also supported in Kotlin that if the last argument to a function is a function, the Lambda expression passed as the corresponding argument can be placed outside the parentheses:
// For example, testHighFun, we can place lambda outside the parentheses
testHighFun(){
//doSomeThing
}
// If the lambda expression is the only argument when called, the parentheses can be completely omitted
testHighFun{
//doSomeThing
}
// It is common for a lambda expression to have only one argument.
// If the compiler can identify the signature itself, it may not declare a unique parameter and ignore ->. This parameter is implicitly declared as it: as follows
testHighFun{
//doSomeThing
it.toString(it)
}
Copy the code
Returns a value from a lambda expression
We can use qualified return syntax to explicitly return a value from lambda. Otherwise, the value of the last expression is implicitly returned. Refer to the official website for examples below
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
Copy the code
Ok, so that’s the basic use of Lambda.
So with all this talk, we’ve just talked about how Lambda works, so what’s the nature of Lambda? In fact, if you think about testHighFun above you can pass in a Lambda expression to get a sense that Lambda is essentially a function type object. This can also be seen by sending compiled Java code.
Anonymous functions and Lambda expressions:
- Both can be passed as arguments to higher-order functions.
- Both are function type objects by nature.
Remarks: the above is my personal understanding of higher-order functions, anonymous functions, Lambda expressions, what is wrong, but also ask you to correct.
Reference article:
- Juejin. Cn/post / 684490…
- Kotlin core programming
- Kotlin website