Detailed Explanation of sam of Functional Interface in Kotlin

  • 2021-11-02 02:34:55
  • OfStack

Using lambda expression to represent anonymous class instances in java

When using java to set listening for a button, we usually create an anonymous class instance, as follows


Button.setOnClickListener(new OnClickListener()){
  @Override
  public void onClick(View v){
    Toast.makeText(this,"Hello World",Toast.LENGTH_LONG).show()
  }
}

In kotlin we can replace this instance by passing an lambda expression


btn_test.setOnClickListener { view : View ->
      Toast.makeText(this,"Hello World",Toast.LENGTH_LONG).show()
}

The reason why it can be implemented in this way is that the OnClickListener interface has only one abstract method, which is regarded as a functional interface in kotlin, or the SAM interface and SAM represent single abstract methods, and similarly, functional interfaces such as Runnable and Callable.

Pass lambda as a parameter to Java method

We define 1 in java


void postponeComputation(int delay,Runnable computation){
}

Then use the lambda expression to pass the Runnable parameter


postponeComputation(1000){
    print("hello world")
}

Note that the lambda argument compiler will automatically convert it to an Runnable instance. The effect of this call is the same as that of the displayed implementation of an Runnable anonymous object


postponeComputation(1000,object : Runnable{
    override fun run(){
        println(42)
    }
})

Instead of creating an Runnable object repeatedly every time this statement is called, using an lambda expression as an argument creates only one object without accessing any function variables that customize it.

If you want complete equivalence, you need to define it like this


val runnable = Runaable{ println(42) }
fun handleComputation(){
   postponeComputation(1000,runnable)
}

In addition, if a variable is captured from the scope that surrounds it, the same instance will not be reused for each invocation, as the following invocation will use a new instance of Runnable each time.


fun handleComputation(id : String){
   postponeComputation(1000){println(id)}
}

In fact, since kotlin 1.0, every lambda has been compiled into an anonymous class. If lambda captures variables, each captured variable will have a corresponding field in the anonymous class.

SAM Constructor: Explicitly converts lambda into a functional interface.

Some methods need to return 1 functional interface, but can not return 1 lambda. You can wrap it with SAM constructor. As follows


fun createAllDoneRunable() : Runnable{
  return Runnable{ println( " All done " ) }
}

The SAM constructor accepts only one parameter-an lambda used as the single abstract method body of the functional interface and returns an instance of the interface class.

In addition, in addition to the return value through lambda to create a functional interface, you can also put the functional interface generated by lambda in a variable, as follows


val listener = OnClickListener{
  view -> val text = when(view.id){
    R.id.button1 ->  " First button " 
    R.id.button2 ->  " Second button " 
    else ->  " Unknown button"
  }
  toast(text)
}
button1.setOnClickListener(listener)
button2.setOnClickListener(listener)

Related articles: