ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# Lambda表达式 ![Lambad](https://img-blog.csdnimg.cn/20200903212309556.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDE4NDk5MA==,size_16,color_FFFFFF,t_70#pic_center) Lambda表达式是JDK8引入的一个新特性,又名匿名函数或闭包,可以不用创建显示的标识符(变量名,函数名,类名等)来书写需要的代码。 java作为一门面向对象的语言,对语法有着严格的要求,在java中”一切皆对象“。但是有时候随着我们写的代码多了又发现写java代码有时候会显得复杂。比如我们每次在写代码时都需创建一个类,用到该类时又要创建该类的实例。有时候我们仅仅只是需要一个功能就能实现我们想要的目的,而不想要去和任何对象绑定在一起。例如定义一个方法,在需要的时候直接使用该方法就行了,就跟C语言这门面向过程的函数定义一样。在C++和python中是同时支持面向过程与面向对象的,不用在写代码的时候每次都需要创建一个类。 **直接使用方法的思想**就是一种函数式编程的思想。这里的“方法”和"函数"并没有太大的区别,很多时候,“方法”这个名词用在面向对象的编程语言中,而“函数”更多用在面向过程的编程语言中,例如C语言。“方法”和“函数”在抽象的层面上讲,都是接受用户的输入,处理数据,最后产生相应的结果。 总之,JDK8中引入的Lambda表达式就是为了能够让我们在一些情况下书写出更简洁的代码。下面举例说明 &nbsp; ## 使用Lambda表达式 ### 复杂的代码 在创建多线程的方法中,我们有以下方法来创建一个线程,即使用Runnable接口重写run方法来实现 ```java Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("这是一个匿名内部类"); } }); thread.start(); //也可以 new Thread(new Runnable() { @Override public void run() { System.out.println("这是一个匿名内部类"); } }).start(); ``` ### 简洁的代码 从上面可以发现,其实我们代码只想执行 System.out.println("这是一个匿名内部类");这一条语句而已,然而每次创建一个线程时需要创建一个Runnable接口的实现对象,就是因为这是按照“面向对象”的编程思想实现的。如果使用Lambda表达式的方法的代码就简洁了许多。 ```java new Thread(()->{ System.out.println("这是一个匿名内部类"); }).start(); ``` **Lambda语法** Lambda表达式的基本语法如下 > () -> {} 1. 前面的一对小括号 **()** 即为run方法的参数,这里run方法本身就不用传递任何参数。 2. 中间的 -**>** 表示将前面的参数传递给后面{}中的代码。 3. **{}** 即表示run方法执行的代码体。 其标准的格式即为: ```java (参数类型 参数名称...) -> { //方法执行的代码 } ``` ## Lambda表达式的省略 Lambda表达式在上述标准的格式下还能进一步的进行省略,可省略的情况如下 - 小括号内的参数类型可以省略。 java是强类型语言,在方法是不允许省略参数的类型的。但是在Lambda表达式中参数的类型是可以省略的,这是得益于java的推测机制,可以通过上下文推断出该变量是什么类型。 - 如果小括号内**只有一个参数**,则可以将括号也给省略了;例如 ```java name -> {System.out.println(name);} ``` 个人觉得这个和上面那个省略一起用程序会比较可读一些,即在只有一个参数的情况下省略变量的类型和括号,有多个参数的情况下变量类型就不要省略了。 - 如果花括号内**有且仅有一条语句**,则无论是否有返回值,可将花括号,return关键字,语句的分号一起省略了;例如 ```java name -> System.out.println(name) ``` ## 什么情况下使用Lambda表达式 虽说使用Lambda表达式可以让代码更加简洁,但是并不能够用在任意的场景下的。就像上面举的例子,用来替换Runnable接口的匿名实现类。即在使用时仍然是要存在一个接口,并且该接口中只有一个方法,不然我们直接用个 **()** 怎么知道调用哪个方法呢?当然这里值得一提的是,匿名实现类在编译的时候仍然会创建该类的.class文件。而Lambda表达式并不会,用反编译的方法发现Lambda表达式被封装成主类的一个私有方法,具体请看<a href='https://objcoding.com/2019/03/04/lambda/'>关于java Lambda这篇就够了</a>. 同样的,在JDK8中另外一个新特性函数性接口的概念可以和Lambda表达式很好的配合使用起来。 &nbsp; ### 函数性接口 这种接口内部只有一个抽象方法待实现(可以有一些默认 default 的方法),例如Runnable接口就只有一个run()方法待实现。Comparator接口中只有一个compare()方法,其他的方法均用default修饰,或static修饰(除了继承自Object的方法)。 该接口定义举例 ```java @FunctionalInterface public interface MyFunctionInterface { public abstract void method(); } ``` **@FunctionalInterface** 注解可以在编译时期将该接口看成是一个函数性接口,加上该注解后如果在该接口中定义了多个抽象方法,就会报错,加上该注解也可以让人很明了这是一个函数性接口。 用lambda实现该接口中的method()方法: ```java public class Demo { /* * 定义一个方法,将自定义的接口作为参数进行传递 */ public static void show(MyFunctionInterface myFunctionInterface) { myFunctionInterface.method(); } public static void main(String[] args) { show(() -> { System.out.println("使用lambda表达式重写接口中的抽象方法!"); }); } } ``` 可见,使用Lambda表达式一般都是将一个函数性接口作为一个参数传递给一个方法(构造方法也行),然后使用Lambda表达式实现该接口对应的抽象方法。 关于函数性接口,java定义了一些常用的供我们配合Lambda使用,在java.util.function包中。 ## 小结 1. 当匿名类为函数式接口时,可以考虑用Lambda表达式简化代码的书写; 2. 虽说Lambda表达式体现了函数式编程思想,但是java还是严格的使用面向对象的思想,所以Lambda表达式并不能任意供我们使用; 3. Lambda表达式的出现是java向 “写更少,做更多” 迈进的巨大一步。