当前位置:网站首页>Introduction to the basics of kotlin language: lambda expression

Introduction to the basics of kotlin language: lambda expression

2022-06-25 20:31:00 Little fish learning Android

What is? Lambda expression ?

Lambda expression , It's actually anonymous functions . And a function is actually a function (function), Anonymous functions , It's anonymous function code . stay Kotlin among , Functions also appear as a type , Although in the current version , The flexibility of function types is not as good as Python Such language , But it can also be assigned and passed , This is mainly reflected in Lambda On the expression .

Let's see one first Lambda Examples of expressions :

 fun main(args: Array<String>) { 
     val lambda = { 
         left: Int, right: Int 
         -> 
         left + right 
     } 
     println(lambda(2, 3)) 
 } 

As you can see, we defined a variable lambda, Assign one Lambda expression .Lambda The expression is enclosed in a pair of braces , Then write down the parameters and their types in turn , If not, don't write , Then write down -> , This indicates that the following is the function body , The expression result of the last sentence of the function body is Lambda Return value of expression , For example, the return value here is the result of parameter summation .

We use () Call this... In the form of Lambda expression , Actually this () The corresponding is invoke Method , let me put it another way , We can write it here :

 println(lambda.invoke(2,3)) 

The two calls are written in exactly the same way .

without doubt , The output of this code should be 5.

simplify Lambda expression

Let's take another example :

 fun main(args: Array<String>) { 
     args.forEach { 
        if(it == "q") return 
        println(it) 
     } 
     println("The End") 
 } 

args Is an array , We've seen it for Example of loop iterative array , But we actually have more modern means to iterate over an array , So let's take this example . There's nothing to be afraid of , Once you tear off its mask , You'll find that you already know it :

 public inline fun <T> forEach(action: (T) -> Unit): Unit { 
     for (element in this) action(element) 
 } 

This is an extension method , The extension method is easy to understand , The original class does not have this method , We extend it externally with a new method , This new method is the extension method . Everyone regards it as Array Just define your own method , We see that there is actually a for Cycle, right ,for What does the cycle do ? Called our incoming Lambda expression , And pass in each element as a parameter . So we call forEach How to write the method ?

 args.forEach({ 
     element -> println(element) 
 }) 

What is the equivalent ?

 for(element in args){ 
    println(element) 
 } 

It's easy to understand ?

next ,Kotlin Let's put the last of the function Lambda Remove the parentheses from the expression parameters , in other words , We can change the above forEach Writing :

 args.forEach(){ 
     element -> println(element) 
 } 

It looks a bit like a function definition , But the difference is obvious . Don't faint at this time , If you feel dizzy, I have motion sickness medicine here. Take some .

It's not over yet , If there is only one function Lambda Expression parameter , The one in front is just , It's no use leaving a parenthesis , Just throw it away :

 args.forEach{ 
     element -> println(element) 
 } 

How are you ? You think it's over ?nonono, If this is passed in Lambda The expression has only one parameter , Or like the one above forEach, There is only one parameter element , So we can also omit it when calling , And by default it is called it, It makes sense , It's not it Well , Although people are actually iterator It means :

 args.forEach{ 
      println(it) 
 } 

Um. , About the same . Is it over , No, . Is it over yet ? There's only one left . If this Lambda There is only one function call in the expression , And the parameter of this function is also this Lambda The argument to the expression , Then you can also simplify the above code by function reference :

 args.forEach(::println) 

Is this a bit like C Function pointer inside ? Functions are also objects , It's no big deal , As long as the arguments, such as println The input parameter and return value of are consistent with the formal parameter requirements , Then it can be simplified .

To sum up :

  1. the last one Lambda Can be moved out
  2. only one Lambda, Parentheses can be omitted
  3. Lambda Only one parameter can be defaulted to it
  4. Enter the reference 、 A function whose return value is consistent with the formal parameter can be passed as an argument by function reference

In this way, we can roughly understand the example we gave before :

 fun main(args: Array<String>) { 
     args.forEach { 
        if(it == "q") return 
        println(it) 
     } 
     println("The End") 
 }

from Lambda Back in

Do you really understand ? Suppose the parameter I enter is

 o p q r s t 

Do you know what to output ?

 o 
 p 
 The End 

Am I right? ?

incorrect ,return It will end directly main function . Why? ?Lambda expression , It's an expression , Although it looks like a function , It's also like a function , But it seems to be just a code block .

that , There's no way return Did you? ? Of course not. , The soldiers came to cover the water :

 fun main(args: Array<String>) { 
     args.forEach [email protected]{ 
        if(it == "q") [email protected] 
        println(it) 
     } 
     println("The End") 
 } 

Just define a label . You can also [email protected] Then add your return value , If necessary .

Lambda The type of expression

good , It was said that Lambda Expressions are actually function types , We're in the front forEach Method Lambda The expression is actually forEach Method , Let's see forEach The definition of :

 public inline fun <T> Array<out T>.forEach(action: (T) -> Unit): Unit { 
     for (element in this) action(element) 
 } 

be aware ,action The type of this parameter is (T) -> Unit, This is Lambda The type of expression , Or the type of function , It means that this function accepts a T Parameters of type , Return to one Unit Result of type . Let's look at a couple more examples :

 () -> Int // No arguments , return  Int  
 (Int, Int) -> String // Two integer parameters , Return string type  
 (()->Unit, Int) -> Unit // Passed in a  Lambda  Expressions and an integer , return  Unit 

We usually use this form to express Lambda The type of expression . Someone might say , Since people are all types , Why don't you have a name ? Or say , Which class does it correspond to ?

 public interface Function<out R> 

In fact, all Lambda Expressions are Function The implementation of the , At this time, if you ask me , that invoke Methods? ? Where was it defined ? Don't be funny to say it ,Kotlin Our developers have defined 23 individual Function Sub interface of , among FunctionN Express invoke There are methods n Parameters ..

 public interface Function0<out R> : Function<R> { 
     public operator fun invoke(): R 
 } 
 public interface Function1<in P1, out R> : Function<R> { 
     public operator fun invoke(p1: P1): R 
 } 
 ... 
 public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> { 
     public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R 
 } 

Frankly speaking , The first time I saw this , I just laughed ,Kotlin The developers are really black humor .

It can't be over like this , In case I really have a function , The parameter exceeds 22 individual , Don't Kotlin Don't you support it ?

 fun hello2(action: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit) { 
     action(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22) 
 } 

So we define a parameter with 23 One of the Lambda expression , Calling methods is also rude :

 hello2 { i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20, i21, i22 -> 
     println("$i0, $i1, $i2, $i3, $i4, $i5, $i6, $i7, $i8, $i9, $i10, $i11, $i12, $i13, $i14, $i15, $i16, $i17, $i18, $i19, $i20, $i21, $i22,") 
 } 

Compile run results :

 Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/Function23 
    at java.lang.Class.getDeclaredMethods0(Native Method) 
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) 
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048) 
    at java.lang.Class.getMethod0(Class.java:3018) 

Sure enough , Although this parameter has 23 One of the Lambda The expression is mapped to kotlin/Function23 This class , however , This class does not exist , in other words , For more than 22 Parameters Lambda expression ,Kotlin The code can be compiled through , But it will throw runtime exceptions . Of course, it's not a thing , After all, who is mentally disabled enough to need parameters 22 More than one ?

SAM transformation

Look at the name, it's very tall , You've probably seen a lot of simple things to fry chicken , You must not avoid such things , Learn one more and you'll be able to bluff one more .

 val worker = Executors.newCachedThreadPool() 
   
 worker.execute { 
     println("Hello") 
 } 

We should have introduced a Runnable Instance , The result is a Lambda Expressions fool the past

 GETSTATIC net/println/MainKt$main$1.INSTANCE : Lnet/println/MainKt$main$1; 
 CHECKCAST java/lang/Runnable 
 INVOKEINTERFACE java/util/concurrent/ExecutorService.execute (Ljava/lang/Runnable;)V 

Look at the three byte codes above , The first sentence gets an instance of a class , This class looks like an anonymous inner class :

 final class net/println/MainKt$main$1 implements java/lang/Runnable  { 
    ... 
 } 

This is the bytecode part of the class definition , Realized Runnable A class of interface !

The second sentence , After you get the instance of this class, do a forced conversion —— What else , Just use it , There must be no problem .

Then you say SAM What are the conditions for conversion ?

  • First , The caller is Kotlin among , The callee is Java Code . If in the previous example worker.execute(...) Is defined in Kotlin Medium method , Then we can't use SAM The conversion .
  • secondly , Parameter must be Java Interface , in other words ,Kotlin Interfaces and abstract classes 、Java Abstract classes are not allowed .
  • Again , Parametric Java The interface must have only one method .

Let's take another example Android Common examples in :

 view.setOnClickListener{ 
    view -> 
    ... 
 } 

view.setOnClickListener(...) yes Java Method , Parameters OnClickListener yes Java Interface , And there's only one way :

 public interface OnClickListener { 
     void onClick(View v); 
 } 

kotlin Data sharing

senior Kotlin Strengthen actual combat

140 Set Kotlin From entry to mastery, the whole series ( Project development practice ) Video tutorial

原网站

版权声明
本文为[Little fish learning Android]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202181403125700.html