jdk1.8新特性第一弹之《Lambda表达式》

tech2024-10-31  16

Lambda表达式

Lambda表达式是JDK8引入的一个新特性,又名匿名函数或闭包,即可以不用创建显示的标识符(函数名,变量名,类名等~)来书写需要的代码。

java作为一门面向对象的语言,对语法有着严格的要求,在java中”一切皆对象“。但是有时候随着我们写的代码多了又发现写java代码有时候会显得复杂。比如我们每次在写代码时都需创建一个类,用到该类时又要创建该类的实例。有时候我们仅仅只是需要一个功能实现我们想要的目的,而不想要去和任何对象绑定在一起。例如定义一个方法,在需要的时候直接使用该方法就行了,就跟C语言这门面向过程的函数定义一样。在C++和python中是支持这样做的,可以在写代码的时候并不用每次都需要创建一个类。

直接使用方法的思想是一种函数式编程的思想。这里的“方法”和"函数"并没有太大的区别,很多时候,“方法”这个名词用在面向对象的编程语言中,而“函数”更多用在面向过程的编程语言中,例如C语言。“方法”和“函数”在抽象的层面上讲,都是接受用户的输入,处理数据,最后产生相应的结果。

总之,JDK8中引入的Lambda表达式就是为了能够让我们在一些情况下书写出更简洁的代码。下面举例说明

使用Lambda表达式

复杂的代码

在创建多线程的方法中,我们有以下方法来创建一个线程,即使用Runnable接口重写run方法来实现

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表达式的方法的代码就简洁了许多。

new Thread(()->{ System.out.println("这是一个匿名内部类"); }).start();

Lambda语法

Lambda表达式的基本语法如下

() -> {}

(刚开始学的时候感觉形式和JS中箭头函数很像)

前面的一对小括号 () 即为run方法的参数,这里run方法本身就不用传递任何参数。中间的 -> 表示将前面的参数传递给后面{}中的代码{} 即表示run方法执行的代码体

其标准的格式即为:

(参数类型 参数名称...) -> { //方法执行的代码 }

Lambda表达式的省略

Lambda表达式在上述标准的格式下还能进一步的进行省略,可省略的情况如下

小括号内的参数类型可以省略

java是强类型语言,在方法是不允许省略参数的类型的。但是在Lambda表达式中参数的类型是可以省略的,这是得益于java的推测机制,可以通过上下文推断出该变量是什么类型。

如果小括号内只有一个参数,则可以将括号也给省略了;例如

name -> {System.out.println(name);}

个人觉得这个和上面那个省略一起用程序会比较可读一些,即在只有一个参数的情况下省略变量的类型和括号,有多个参数的情况下变量类型就不要省略了。

如果花括号内有且仅有一条语句,则无论是否有返回值,可将花括号,return关键字,语句的分号一起省略了;例如

name -> System.out.println(name)

什么情况下使用Lambda表达式

虽说使用Lambda表达式可以让代码更加简洁,但是并不是随意可以用在任意的场景下的。就像上面举的例子,用来替换Runnable接口的匿名实现类。即在使用时仍然是要存在一个接口,并且该接口中只有一个方法,不然我们直接用个 () 怎么知道调用哪个方法呢?当然这里值得一提的是,匿名实现类在编译的时候仍然会创建该类的.class文件。而Lambda表达式并不会,看大佬用反编译的方法发现Lambda表达式被封装成主类的一个私有方法,具体请看关于java Lambda这篇就够了.

同样的,在JDK8的新特性中一个函数性接口的概念可以和Lambda表达式很好的配合使用起来。

函数性接口

这种接口内部只有一个抽象方法待实现(可以有一些默认 default 的方法),例如Runnable接口就只有一个run()方法待实现。Comparator接口中只有一个compare()方法,其他的方法均用default修饰,或static修饰(除了继承自Object的方法)。

该接口定义举例

@FunctionalInterface public interface MyFunctionInterface { public abstract void method(); }

@FunctionalInterface 注解可以在编译时期将该接口看成是一个函数性接口,加上该注解后如果在该接口中定义了多个抽象方法,就会报错,加上该注解也可以让人很明了这是一个函数性接口。

用lambda实现该接口中的method()方法:

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包中。后面会讲一些常用的函数性接口~

小结

当匿名类为函数式接口时,可以考虑用Lambda表达式简化代码的书写;虽说Lambda表达式体现了函数式编程思想,但是java还是严格的使用面向对象的思想,所以Lambda表达式并不能任意供我们使用;个人感觉Lambda表达式的出现是java向 “写更少,做更多” 迈进的巨大一步。
最新回复(0)