Android advanced Kotlin high-order functions and Lambda expressions detailed explanation

Android advanced Kotlin high-order functions and Lambda expressions detailed explanation

[[415968]]

Preface

Lambda syntax has been widely used in Java. We also integrate Lambda plug-ins into almost every project we develop on Android, because Lambda can indeed reduce the amount of code.

Coincidentally, Kotlin also has Lambda syntax. This article will explain in detail how to write and use Lambda syntax.

1. Detailed explanation of Kotin high-order functions

1. A higher-order function is a function that takes a function as an argument or return value. A good example of such a function is lock(), which accepts a lock object and a function, acquires the lock, runs the function, and releases the lock:

  1. fun <T> lock(lock: Lock, body: () -> T): T {
  2. lock.lock()
  3. try {
  4. return body()
  5. }
  6. finally {
  7. lock.unlock()
  8. }
  9. }

body has function type: () -> T, so it should be a function that takes no arguments and returns a value of type T. It is called inside the try{: .keyword }-block, protected by lock, and its result is returned by the lock() function. If we want to call the lock() function, we can pass another function to it as an argument (see function reference)

2. If a function receives another function as a parameter, or the type of the return value is another function, then the function is called a higher-order function

Function type, the basic rules are as follows:

(String,Int) -> Unit

Now add the above function type to the parameter declaration or return value declaration of a function, then this function becomes a higher-order function, for example

fun example(func: (String, Int) -> Unit) {

func("hello", 123)

}

You can see that the example() function here receives a function type parameter, so the example() function is a higher-order function;

Here I am going to define a higher-order function called num1AndNum2(), which accepts two integer and one function type parameters. In the num1AndNum2() function, we will perform some operation on the two integer parameters passed in and return the result. But the specific operation is determined by the function type parameter passed in.

Create a new file named Test1.kt

  1. fun num1AndNum2(num1: Int ,num2: Int ,operation:( Int , Int )-> Int ): Int {
  2. val result = operation(num1,num2)
  3. return result
  4. }
  5. fun plus(num1: Int , num2: Int ): Int {
  6. return num1 + num2
  7. }
  8. fun minus(num1: Int , num2: Int ): Int {
  9. return num1 - num2
  10. }
  11. main() function
  12. fun main(){
  13. val num1 = 100
  14. val num2 = 80
  15. val result1 = num1AndNum2(num1,num2, ::plus)
  16. val result2 = num1AndNum2(num1,num2,::minus)
  17. println( "result1:" +result1)
  18. println( "result2:" +result2)
  19. }
  20. result1:180
  21. result1:20

The writing of ::plus and ::minus is a function reference, which means passing the plus() and minus() functions as parameters to the num1AndNum2() function.

If every time you call any higher-order function, you have to define a function that matches its function type parameter, isn't it too complicated? That's right, so Kotlin also supports many other ways to call higher-order functions, such as Lambda expressions, anonymous functions, member references, etc. Lambda expressions are the most common and most popular way to call higher-order functions. The code just now uses Lambda expressions to implement (the last line of the Lambda expression is automatically used as the return value), and the plus() and minus() functions can be deleted.

  1. fun main() {
  2. val num1 = 100
  3. val num2 = 80
  4. val result1 = num1AndNum2(num1, num2) { n1, n2 ->
  5. n1 + n2
  6. }
  7. val result2 = num1AndNum2(num1, num2) { n1, n2 ->
  8. n1 - n2
  9. }
  10. println( "result1:" + result1)
  11. println( "result2:" + result2)
  12. }
  13. fun num1AndNum2(num1: Int , num2: Int , operation: ( Int , Int ) -> Int ): Int {
  14. val result = operation(num1, num2)
  15. return result
  16. }

3. Closure function

The return value of a function is a function, and the function contains another function inside, which can be an anonymous function with or without parameters.

  1. fun main(args: Array<String>) {
  2. val mm = aaa()
  3. println(mm())
  4. println(mm())
  5. println(mm())
  6. println(mm())
  7. println(mm())
  8. val kk = bbb()
  9. println(kk( "shadow" )) //shadow --- 1  
  10. println(kk( "shadow" )) //shadow --- 2  
  11. println(kk( "shadow" )) //shadow --- 3  
  12. println(kk( "shadow" )) //shadow --- 4  
  13. println(kk( "shadow" )) //shadow --- 5  
  14. }
  15. //The closure function is the function as the return parameter
  16. fun aaa(): () -> ( Int ) {
  17. var current = 10
  18. return fun(): Int {
  19. return   current ++
  20. }
  21. }
  22. fun bbb(): (String) -> (String) {
  23. var current = 0;
  24. return fun(str: String): String {
  25. current ++;
  26. return   "$str --- $current" ;
  27. }
  28. }

4. Kotin high-order function examples

Map Transformation

  1. fun main(args: Array<String>) {
  2. val list = listOf(1, 2, 3, 4, 5, 6)
  3. val newList = list.map {
  4. //Operate the data in the collection and assign it to the new collection
  5. (it * 2).toString()
  6. }.forEach(::println) //2 4 6 8 10 12
  7. val doubleList = list.map {
  8. it.toDouble()
  9. }.forEach(::print) //1.0 2.0 3.0 4.0 5.0 6.0
  10. //The second way to call the function as a parameter class name:: method name
  11. val doubleList2 = list.map( Int ::toDouble).forEach(::print) ////1.0 2.0 3.0 4.0 5.0 6.0
  12. }

flatMap transforms a collection of collections

  1. fun main(args: Array<String>) {
  2. val list = arrayOf(
  3. 1..5,
  4. 50..55
  5. )
  6. //Convert multiple array sets into one array and transform the data
  7. val mergeList = list.flatMap { intRange -> //set within a set 1..5 , 50..55
  8. intRange.map { intElement -> //Traversal of collection within collection 1,2,3,4,5
  9. "No.$intElement"  
  10. }
  11. }
  12. // No .1 , No .2 , No .3 , No .4 , No .5 , No .50 , No .51 , No .52 , No .53 , No .54 , No .55 ,
  13. mergeList.forEach { print( "$it , " ) }
  14. println()
  15. //Directly transform multiple array sets into a node set
  16. val newList = list.flatMap {
  17. it
  18. }
  19. //1 , 2 , 3 , 4 , 5 , 50 , 51 , 52 , 53 , 54 , 55 ,
  20. newList.forEach { print( "$it , " ) }
  21. }

filter

  1. fun main(args: Array<String>) {
  2. val list = arrayOf(
  3. 1..5,
  4. 2..3
  5. )
  6. val newList = list.flatMap {
  7. it
  8. }
  9. //Filter items with data > 2 in the collection
  10. val filterList = newList.filter { it > 2 }
  11. filterList.forEach(::print) //3453
  12. //Filter the items whose subscripts are odd in the collection
  13. val filterIndexList = newList.filterIndexed { index , i -> index % 2 == 0; }
  14. filterIndexList.forEach { print(it) } //1 3 5 3
  15. }

forEach

  1. fun main(args: Array<String>) {
  2. var list = listOf(1, 2, 3, 4, 5, 6)
  3. list.forEach(::println)
  4. val newList = arrayListOf<String>() --->1,2,3,4,5,6  
  5. list.forEach {
  6. newList. add ((it * 2).toString()) --->2,4,6,8,10,12  
  7. }
  8. newList.forEach(::println)
  9. }

Next, we will introduce Lambda

2. Lambda Expression Detailed Explanation

1. What is Lambda Expression?

  • Lambda expression is an important new feature introduced by JDK8. Although it looks very impressive, the essence of Lambda expression is just a "syntactic sugar". If you are used to the idea of ​​object-oriented programming, you may not be used to this syntax at first. But if you have learned C#, you will find that the syntax is very similar to the "delegate" in C#.
  • As we all know, everything in Java is an object. Java has always been committed to maintaining its object-first feature. Although functions are important to Java, in the Java world, functions cannot exist independently and can only rely on objects to call. In functional programming languages, functions are first-class citizens. They can exist independently. You can assign them to a variable or pass them as parameters to other functions. JavaScript is the most typical representative of functional programming languages;
  • Functional languages ​​provide a powerful feature - closures, which have many advantages over traditional programming methods. Closures are callable objects that record some information from the scope in which they are created. Therefore, the closest concept to closures provided by Java is Lambda expressions. Although there are significant differences between closures and Lambda expressions, at least Lambda expressions are a good substitute for closures.
  • The purpose of using Lambda expressions is to replace most anonymous inner classes, so that we can write more concise and elegant Java code, especially in the traversal of collections and other collection operations, which can greatly optimize the code structure. JDK also provides a large number of built-in functional interfaces for us to use, making the use of Lambda expressions more convenient and efficient;
  • If you are not proficient in Lambda expressions, it is not recommended to use them indiscriminately, because you can also achieve the corresponding functions without using Lambda expressions. Just treat it as a tool to add icing on the cake.

2. Lambda expression syntax

The basic syntax structure of Lambda expressions is as follows:

  1. (parameters) -> expression
  2. or
  3. (parameters) ->{ statements; }

Among them, () is used to describe the parameter list, {} is used to describe the method body, -> is the lambda operator, read as (goes to), parameters represents parameters, expression represents expression, and statements represents code blocks.

The structure is described as follows:

A lambda expression can have zero or more parameters.

The types of parameters can be either explicitly stated or inferred from the context.

For example:

(int a) has the same effect as (a)

All parameters must be enclosed in parentheses and separated by commas.

For example:

  1. (a, b) or ( int a, int b) or (String a, int b, float c)

Empty parentheses indicate an empty parameter set. For example: () -> 42

When there is only one parameter and its type can be inferred, the parentheses () can be omitted.

For example: a -> return a*a

The body of a lambda expression can contain zero or more statements.

If the body of a Lambda expression has only one statement, the curly braces {} can be omitted. The return type of the anonymous function is consistent with the body expression;

If the body of a Lambda expression contains more than one statement, the expression must be contained in curly braces {} (forming a code block). The return type of the anonymous function is consistent with the return type of the code block, or null if there is no return;

3. Important features of Lambda expressions

  • Optional type declaration: There is no need to declare parameter types, and the compiler can uniformly identify parameter values.
  • Optional parameter parentheses: One parameter does not need to define parentheses, but multiple parameters do.
  • Optional curly braces: If the body contains a single statement, no curly braces are required.
  • Optional return keyword: If the body has only one expression that returns a value then the compiler will automatically return that value. The curly braces are required to specify that the expression returns a value.

4. Detailed explanation of functional interface

①What is a functional interface?

A functional interface in Java refers to an interface with only one abstract method.

Functional interface is an interface suitable for functional programming scenarios. Functional programming in Java is reflected in Lambda, so functional interface is an interface that can be used by lambda. Only when there is one and only one abstract method in the interface can lambda be deduced smoothly;

②Grammar

@FunctionalInterface annotation

The annotation is similar to the @override annotation and is applied to the definition of a functional interface.

Once this annotation is used to define a functional interface, the compiler will check whether the interface has one and only one abstract method.

  1. @FunctionalInterface
  2. public interface MyFunctionalInterface {
  3. void myMethod();
  4. }
  5. Using functional interfaces as method parameters
  6. public class Demo {
  7. private static void dos(FunctionInterface fi){
  8. fi.method();
  9. }
  10. public   static void main(String[] args) {
  11. Demo.dos(()->{System. out .println( "lambda expression" );});
  12. }

5. Basic use cases of Lambda expressions

Basic use cases of expressions

  1. // 1. No parameters are required, the return value is 5
  2. () -> 5
  3. // 2. Receive a parameter (number type) and return twice its value
  4. x -> 2 * x
  5. // 3. Accept 2 parameters (numbers) and return their difference
  6. (x, y) -> x – y
  7. // 4. Receive 2 int integers and return their sum
  8. ( int x, int y) -> x + y
  9. // 5. Accept a string object and print it in the console, without returning any value (it looks like void)
  10. (String s) -> System. out .print(s)
  11. Define 6 interfaces. We will demonstrate cases based on these 6 interfaces later.
  12. /**Multiple parameters have no return*/
  1. @FunctionalInterface
  2. public interface NoReturnMultiParam {
  3. void method( int a, int b);
  4. }
  5. /**No parameters, no return value*/
  6. @FunctionalInterface
  7. public interface NoReturnNoParam {
  8. void method();
  9. }
  10. /**One parameter has no return*/
  11. @FunctionalInterface
  12. public interface NoReturnOneParam {
  13. void method( int a);
  14. }
  15. /**Multiple parameters have return values*/
  16. @FunctionalInterface
  17. public interface ReturnMultiParam {
  18. int method( int a, int b);
  19. }
  20. /** No parameters but return*/
  21. @FunctionalInterface
  22. public interface ReturnNoParam {
  23. int method();
  24. }
  25. /**A parameter has a return value*/
  26. @FunctionalInterface
  27. public interface ReturnOneParam {
  28. int method( int a);
  29. }
  30. Example code:
  31. public class Test2 {
  32. public   static void main(String[] args) {
  33. //1. Simplify the parameter type. You can omit the parameter type, but you must omit all parameters.
  34. NoReturnMultiParam lamdba1 = (a, b) -> {
  35. System. out .println( "Simplified parameter type" );
  36. };
  37. lamdba1.method(1, 2);
  38. //2. Simplify the parameter brackets. If there is only one parameter, the parameter brackets can be omitted
  39. NoReturnOneParam lambda2 = a -> {
  40. System. out .println( "Simplified parameter parentheses" );
  41. };
  42. lambda2.method(1);
  43. //3. Simplify the method body braces. If the method has only one statement, the method body braces can be omitted, similar to if or for  
  44. NoReturnNoParam lambda3 = () -> System. out .println( "Simplified method body braces" );
  45. lambda3.method();
  46. //4. If the method body has only one statement and it is a return statement, the method body braces can be omitted
  47. ReturnOneParam lambda4 = a -> a+3;
  48. System. out .println(lambda4.method(5));
  49. ReturnMultiParam lambda5 = (a, b) -> a+b;
  50. System. out .println(lambda5.method(1, 1));
  51. }
  52. }

6. Use lambda expressions to reference methods

① The syntax for referencing a method is: method owner:: method name

Note: The static method belongs to the class name, and the common method belongs to the object. This code example combines the custom functional interface at the top:

  1. public class Exe1 {
  2. public   static void main(String[] args) {
  3. ReturnOneParam lambda1 = a -> doubleNum(a);
  4. System. out .println(lambda1.method(3));
  5. //lambda2 references the already implemented doubleNum method
  6. ReturnOneParam lambda2 = Exe1::doubleNum;
  7. System. out .println(lambda2.method(3));
  8. Exe1 exe = new Exe1();
  9. //lambda4 references the already implemented addTwo method
  10. ReturnOneParam lambda4 = exe::addTwo;
  11. System. out .println(lambda4.method(2));
  12. }
  13. /**
  14. * Require
  15. * 1. The number and type of parameters must be consistent with those defined in the interface
  16. * 2. The return value type must be consistent with that defined in the interface
  17. */
  18. public   static   int doubleNum( int a ) {
  19. return a * 2;
  20. }
  21. public   int addTwo( int a ) {
  22. return a + 2;
  23. }
  24. }

②Constructor reference

Generally, we need to declare an interface, which serves as an object generator, instantiates an object through the class name::new, and then calls a method to return the object.

  1. interface ItemCreatorBlankConstruct {
  2. Item getItem();
  3. }
  4. interface ItemCreatorParamContruct {
  5. Item getItem( int id, String name , double price);
  6. }
  7. public class Exe2 {
  8. public   static void main(String[] args) {
  9. ItemCreatorBlankConstruct creator = () -> new Item();
  10. Item item = creator.getItem();
  11. ItemCreatorBlankConstruct creator2 = Item::new;
  12. Item item2 = creator2.getItem();
  13. ItemCreatorParamContruct creator3 = Item::new;
  14. Item item3 = creator3.getItem(119, "computer" , 5888.88);
  15. }
  16. }

One major difference between using anonymous classes and Lambda expressions is the use of keywords.

For anonymous classes, the keyword this is interpreted as the anonymous class, and for lambda expressions, the keyword this is interpreted as the outer class in which the lambda is written.

7. Lambda Summary Again

  • Function type with no parameters and no return value (Unit return type cannot be omitted): () -> Unit;
  • Function type that receives a parameter of type T and has no return value: (T) -> Unit;
  • Function type that receives parameters of type T and type A and has no return value (same for multiple parameters): (T, A) -> Unit;
  • A function type that takes a parameter of type T and returns a value of type R: (T) -> R;
  • Function type that receives parameters of type T and type A and returns a value of type R (same for multiple parameters): (T, A) -> R;
  • In the method implementation in curly braces {}, if there is only one parameter, you can use it to specify it without writing x->...;
  • If there are multiple parameters in the method implementation, you need to specify the custom parameter name after -> in this way;

Summarize

1. Lambda and higher-order functions are a bit confusing to understand, and require a lot of practice and experimentation to slowly understand

2. Lambda is very deep. Let’s learn and improve together.

<<:  Google will no longer support Google services on Android 2.3.7 and below

>>:  WeChat for Windows 3.3.5 official version released: group chat @ everyone, various details optimized

Recommend

Rat head found in the cafeteria again? This time it's not...

Recently, I saw the news online that a "rat ...

What keeps users in your product?

Introduction I once used a canvas belt for six ye...

Why is your phone still running low on battery?

Many of today's mobile phones have battery ca...

There is actually a Mars-like area on Earth. Where is it?

Apart from Earth, which planet is most familiar t...

Should I learn Swift or Objective-C directly?

[[120413]] After we released the Swift language l...

WeChat-assisted order-taking platform, is WeChat-assisted 80 per order reliable?

WeChat is something that everyone is familiar wit...

3000 words to understand Weibo operation and promotion routines!

Why do I operate Weibo? Because browsing Weibo is...

Do you want to be on the trending list? Read this before deciding

If a product with good ASO is like a famous celeb...