• Using lambda expressions can make the code more concise and efficient
  • Lambda combined with kotlin higher-order functions can simplify the interface callback of traditional Java

Preface

  • Kotlin is very convenient, but sometimes it is also very troublesome, and the more convenient places are, the more troublesome, such as lambda expressions. Many people are attracted to kotlin because of lambda, but many people are scared away by kotlin because of lambda. In fact, most people who have used kotlin for a long time can only use lambda simply. If you don’t use the automatic completion function of Android studio, you won’t write lambda at all. However, to understand lambda, we must first start with kotlin’s “higher-order function”.

Higher-order function

  • In Java, if a function wants to call B function, it can be called directly. If you want to dynamically set the parameters of method B when calling function a, you need to pass in a parameter in function a.

    1. int a(int num){
    2. return b(num) + 1;
    3. }
    4. int b(int num){
    5. return 1;
    6. }
    7. a(10); // -> The B function is called inside the a function
  • But what you want to dynamically set is not the parameters of the function, but a function called from elsewhere. For example, I have a call to other functions inside A. This function may be B, C, or other functions.This function only knows one parameter type, such as int, and one return value type, such as int or void.

    int a(??? method){
      return method() + 1;
    }
    int b(int num){
      return 1;
    }
    int c(int num){
      return 2;
    }
    

    The above method is not allowed in java!!

  • However, when a function is executed, I want to choose which method to call. I hope it can be set dynamically. In other words, I want to pass a function as an argument to another function to another method.Can this be achieved? may not! However, it can also be achieved! ```java int a(Wrapper wrapper){ return wrapper.method(); }

interface Wrapper{ int method(); }

Wrapper wrapper = new Wrapper() { @Override public int method() { return 0; } }; void performA(){ a(wrapper); }

    **We can use the interface to wrap the function, and then pass the type of the interface to the parameter type of the function. When calling the method, we pass the object of the interface as the parameter.If you don't understand here, take another example:**

- **View.setOnClickListener()**
```java
View.OnClickListener onClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        doSomething();
    }
};
button.setOnClickListener(onClickListener);
Here, a view. Onclicklistener type is passed, which essentially passes a function that can be called later: onClick(). Because Java does not allow methods to be passed, we wrap it in an object to pass. In kotlin, **function parameters can also be function types! **When a function has a parameter of function type, if it is called, an object of function type must be passed, and such a function is called "higher-order function". What exactly is a **function type object? Yes, it's a lambda expression!**

Lambda Expressions

  • Lambda expression is essentially an object, and the return value can be treated as a regular object. For example, string, int, float, livedata, etc.It is emphasized here that the lambda expression returns the execution result of the last line of code by default, and return cannot be used.
    lateinit var valueOne:() -> Unit   // -> Object of function type with no function parameter and return value of void
    lateinit var valueTwo:(String,Int) -> String  // -> There are two parameters. The parameter types are string and int function objects
    ....
    fun performValueOne(){
      valueOne = {
          Log.e("performValueOne", "valueOne has been initialized")
      }
      valueOne()  // Log: valueOne has been initialized
    }
    fun performValueTwo(){
      valueTwo = { paramsOne:String,paramsTwo:Int ->
          "performValueTwo has been initialized"
      }
    }
    
    By analogy, it can also be regarded as the return value of a function,My understanding is that lambda itself is a value. The value type is void or string, int or other types. ```kotlin fun lambdaValue(values:(Int) -> Unit):(Int) -> Unit{ return values } // -> Grammar sugar fun lambdaValueOne(values:(Int) -> Unit) = values

fun lambdaValueTwo(values:(Int) -> Unit):(Int) -> Unit{ values(10) // However, when executed here, the following performlambdavaluetwo function will print the number 10 passed in the past return values }

fun performLambdaValueTwo(){
lambdaValueTwo({ values:Int ->
Log.e(TAG, “$values”)
})
} // If the parameter of the higher-order function is the last one, it can be written directly outside the parentheses. If there is only one parameter, “()” can be omitted directly and “{}” can be used directly fun performLambdaValueTwo(){
lambdaValueTwo{values:Int -> Log.e(TAG, “$values”) } }

    **In addition, lambda can be assigned to a variable in addition to being the parameter and return value type of the function. However, for an ordinary function, you must add "::".**
```kotlin
fun a(funParam:(Int) -> String):String{
    return funParam(10)
}
fun b(params:Int):String{
    return params.toString()
}
fun perform(){
    val d = ::b
    a(d) 
}
 **Why add '::'? The official document states that this is called "function reference".What does that mean? **<br />**Recall that lambda is essentially an object. Add "::" to indicate the existence of turning a function into an object. Because only objects can be passed as parameters, and the same is true for assigning values to variables. Only objects can be assigned to variables. However, the nature of kotlin function determines that it can not be regarded as an object. What should we do? Kotlin's choice is to create an object that has the same function as the function. Is to use "::".**<br />**In kotlin, a function is added with "::" which will not represent the function itself, but an object or a reference to an object. But this object is not the function itself, but an object with the same function as this function. Use this object as you use the function.**
fun b(params:Int):String{
    return params.toString()
}
fun perform(){
    val d = ::b
    d(1)
    d.invoke(1)
    (::b)(1)
    (::b).invoke(1)
}
**Again, after using "::" it will not be a function, but an object, an object of function type. Object cannot be called with '()', while object of function type can.** **But what is really called is the invoke function of this object.**
  • Example

    val a = {x:Int,y:String ->
      Log.e("", "Nice to meet you")
      10
    }
    Log.e("", "${a(10,"Shiki")}")  
    // ->
    // Nice to meet you
    // 10
    
  • Item click event of recyclerview

    class Adapters(private val itemClick:(String) -> Unit):ListAdapter<String,Adapters.ViewHolder>(
      object : DiffUtil.ItemCallback<String>() {
          override fun areItemsTheSame(oldItem: String, newItem: String) = oldItem === newItem
          @SuppressLint("DiffUtilEquals")
          override fun areContentsTheSame(oldItem: String, newItem: String) = oldItem == newItem
      }
    ){
      inner class ViewHolder(binding: HomeBannerItemBinding):RecyclerView.ViewHolder(binding.root){
          val text = binding.text
          fun bind(str:String){
    
          }
      }
    
      override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
          HomeBannerItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)).apply { 
              text.setOnClickListener { 
                  itemClick(getItem(absoluteAdapterPosition))
              }
      }
    
      override fun onBindViewHolder(holder: ViewHolder, position: Int) {
          holder.bind(getItem(position))
      }
    }
    
    class HomeFragment : Fragment(R.layout.fragment_home) {
      private val binding: ViewBinding by bindView()
      private val rvAdapter = HomeRvAdapter()
      private val adapter = Adapters{ str:String ->
    
      }
    
      companion object {
          const val TAG = "HomeFragment"
      }
    }