参考链接:
Lambda表达式本质上是一段匿名内部类,也可称为闭包,我们可以将Lambda表达式理解为一段可以传递的代码(将代码像数据一样传递),使用它可以写出简洁、灵活的代码,作为一种更紧凑的代码风格,使java语言表达能力得到提升。
Lambda表达式在java语言中引入了一种新的语法元素和操作,允许把函数作为一个方法的参数(函数作为参数传递进方法中),Lambda表达式操作符为“->”(也称箭头操作符)。它将Lambda表达式分割为两部分, 左边:指Lambda表达式的所有参数,右边:指Lambda体,即表示Lambda表达式需要执行的功能。即:传入参数->利用参数执行功能。
lambda表达式的重要特征如下:
-
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
-
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
-
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
-
可选的返回关键字:如果主体只有一个表达式返回值,则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
0. Lambda标准格式
Lambda省去了面向对象的条条框框,Lambda的标准格式格式由3个部分组成:
(参数类型 参数名称) -> {
代码体;
}
格式说明:
-
(参数类型 参数名称):参数列表
-
{代码体;}:方法体
-
-> :箭头:分隔参数列表和方法体
0.1 使用前提条件
Lambda的语法非常简洁,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:
-
方法的参数或局部变量类型必须为接口才能使用Lambda
-
接口中有且仅有一个抽象方法
举例:
-
设计一个接口,接口中定义一个抽象方法
public interface Flyable { public abstract void flying(); }
-
测试类
public class TestFlyable { public static void main(String[] args) { test01(() -> { }); Flyable s = new Flyable() { @Override public void flying() { } }; Flyable s2 = () -> { }; } public static void test01(Flyable fly) { fly.flying(); } }
0.2 省略形式
在Lambda标准格式的基础上,使用省略写法的规则为:
-
小括号内参数的类型可以省略
-
如果小括号内有且仅有一个参数,则小括号可以省略
-
如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号
举例:
(int a) -> { return new Student(); }
省略后
a -> new Student()
0.3 其它
-
lambda表达式访问局部变量 ,变量的引用可以不用final来修饰 ,但作用与final一样,不能被改变。
-
lambda表达式中不允许内参数与外参数命名相同
0.4 Lambda和匿名内部类对比
-
所需的类型不一样
-
匿名内部类需要的类型可以是类、抽象类、接口
-
Lambda表达式需要的类型必须是接口
-
-
抽象方法的数量不一样
-
匿名内部类所需的接口中抽象方法的数量随意
-
Lambda表达式所需的接口只能有一个抽象方法
-
-
实现原理不同
-
匿名内部类是在编译后会形成class
-
Lambda表达式是在程序运行的时候动态生成class
-
具体使用如下:
1. 无输入参数,无返回值
格式:()->
举例说明:
package lambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @ClassName Test01 * @Description TODO * @Author Jiangnan Cui * @Date 2022/9/18 9:43 * @Version 1.0 */ public class Test01 { // 使用log4j需要导入slf4j-log4j12、log4j的jar包,同时编写log4j的配置文件log4j.properties private static Logger log = LoggerFactory.getLogger(Test01.class); public static void main(String[] args) { /** * Runnable接口是线程辅助类,仅定义了一个方法run()方法 * 并且不接受任何参数,不返回任何值。 */ // 使用匿名内部类形式创建Runnable实现类 Runnable t1 =new Runnable(){ @Override public void run(){ log.info("我是没有使用Lambda表达式:不简洁"); } }; // 使用lambda表达式简化匿名内部类形式创建Runnable实现类 Runnable t2 = () -> log.info("我是使用Lambda表达式:简洁、灵活"); t1.run(); t2.run(); } }
输出结果:
[main] INFO lambda.Test01 - 我是没有使用Lambda表达式:不简洁 [main] INFO lambda.Test01 - 我是使用Lambda表达式:简洁、灵活
附:
-
log4j依赖
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
log4j.properties
# Configure logging for testing: optionally with log file #log4j.rootLogger=debug,appender log4j.rootLogger=info,appender #log4j.rootLogger=error,appender #\u8F93\u51FA\u5230\u63A7\u5236\u53F0 log4j.appender.appender=org.apache.log4j.ConsoleAppender #\u6837\u5F0F\u4E3ATTCCLayout log4j.appender.appender.layout=org.apache.log4j.TTCCLayout
2. 一个输入参数,无返回值
格式:(a)->
举例说明:
package lambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Consumer; /** * @ClassName Demo03 * @Description TODO * @Author Jiangnan Cui * @Date 2022/9/18 10:12 * @Version 1.0 */ public class Demo03 { private static Logger log = LoggerFactory.getLogger(Demo03.class); public static void main(String[] args) { // 使用匿名内部类形式创建Consumer实例 Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String s) { log.info(s); } }; consumer.accept("爱与被爱的区别"); // 使用lambda表达式简化匿名内部类形式创建Consumer实例 Consumer<String> consumer1 = (s)->log.info(s); consumer1.accept("接受爱不一定爱对方,爱一定付出真心爱"); } }
输出结果:
[main] INFO lambda.Demo03 - 爱与被爱的区别 [main] INFO lambda.Demo03 - 接受爱不一定爱对方,爱一定付出真心爱
3. 一个输入参数时,输入参数外面()可以省略
格式:a->
// 使用lambda表达式简化匿名内部类形式创建Consumer实例 Consumer<String> consumer2 = s->log.info(s); consumer2.accept("无论结果怎样,请相信爱,放手去爱");
输出结果:
[main] INFO lambda.Demo03 - 无论结果怎样,请相信爱,放手去爱
4. 两个输入参数,无返回值
格式:(a,b)->
举例说明:
package lambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Comparator; public class Demo05 { private static Logger log = LoggerFactory.getLogger(Demo05.class); public static void main(String[] args) { CompareOldMethod(12,10); findMaxValue(12,10); findMinValue(12,10); } // 使用匿名内部类形式创建Comparator实例比较大小 public static void CompareOldMethod(int num1,int num2){ Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { log.info("o1:{}",o1); log.info("o2:{}",o2); return o1 < o2 ? o2 : o1; } }; log.info("OldFindMaxValue:{}",comparator.compare(num1,num2)); } // 使用lambda表达式简化匿名内部类形式创建Comparator实例比较大小 public static void findMaxValue(int num1,int num2){ Comparator<Integer> comparatorMax = (o1, o2) ->{ log.info("o1:{}",o1); log.info("o2:{}",o2); return o1 < o2 ? o2 : o1; }; log.info("findMaxValue:{}",comparatorMax.compare(num1,num2)); } public static void findMinValue(int num1,int num2){ Comparator<Integer> comparatorMin = (o1,o2)->{ log.info("o1:{}",o1); log.info("o2:{}",o2); return o1 < o2 ? o1 : o2; }; log.info("findMinValue:{}",comparatorMin.compare(num1,num2)); } }
输出结果:
[main] INFO lambda.Demo05 - o1:12 [main] INFO lambda.Demo05 - o2:10 [main] INFO lambda.Demo05 - OldFindMaxValue:12 [main] INFO lambda.Demo05 - o1:12 [main] INFO lambda.Demo05 - o2:10 [main] INFO lambda.Demo05 - findMaxValue:12 [main] INFO lambda.Demo05 - o1:12 [main] INFO lambda.Demo05 - o2:10 [main] INFO lambda.Demo05 - findMinValue:10
5. lambda体只有一条语句时,return和{}均可省略
举例说明:输出结果同上
package lambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Comparator; public class Demo05 { private static Logger log = LoggerFactory.getLogger(Demo05.class); public static void main(String[] args) { CompareOldMethod(12,10); findMaxValue(12,10); findMinValue(12,10); } // 使用匿名内部类形式创建Comparator实例比较大小 public static void CompareOldMethod(int num1,int num2){ Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { log.info("o1:{}",o1); log.info("o2:{}",o2); return o1 < o2 ? o2 : o1; } }; log.info("OldFindMaxValue:{}",comparator.compare(num1,num2)); } // 使用lambda表达式简化匿名内部类形式创建Comparator实例比较大小 public static void findMaxValue(int num1,int num2){ Comparator<Integer> comparatorMax = (o1, o2) -> o1 < o2 ? o2 : o1; log.info("findMaxValue:{}",comparatorMax.compare(num1,num2)); } public static void findMinValue(int num1,int num2){ Comparator<Integer> comparatorMin = (o1,o2)->o1 < o2 ? o1 : o2; log.info("findMinValue:{}",comparatorMin.compare(num1,num2)); } }
6. 类型判断
举例说明:
package lambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Consumer; public class Demo07 { private static Logger log = LoggerFactory.getLogger(Demo07.class); public static void main(String[] args) { dateType(); } public static void dateType(){ // 指明输入参数为String类型 Consumer<String> consumer = (String s) -> log.info(s); consumer.accept("我是指定了输入类型的"); // 不指明输入参数类型 Consumer<String> consumer1 = (s) -> log.info(s); consumer1.accept("我没有指定输入类型哦"); } }
输出结果:
[main] INFO lambda.Demo07 - 我是指定了输入类型的 [main] INFO lambda.Demo07 - 我没有指定输入类型哦
结论:数据类型可以省略,因为编译器可以推断得出,成为“类型推断”。
7. 举例
7.1 创建线程
当需要启动一个线程去完成任务时,通常会通过 Runnable 接口来定义任务内容,并使用 Thread 类来启动该线程。
传统写法:
package com.zhq.test; public class Lambda01 { public static void main(String[] args) { /* 一、使用匿名内部类存在的问题 public Thread(Runnable target) 二、匿名内部类做了哪些事情 1.定义了一个没有名字的类 2.这个类实现了Runnable接口 3.创建了这个类的对象 以上可以看出: 1.使用匿名内部类语法是很冗余的 2.其实我们最关注的是run方法和里面要执行的代码. */ new Thread(new Runnable() { @Override public void run() { System.out.println("匿名内部类方式"); } }).start(); } }
由于面向对象的语法要求,首先创建一个 Runnable 接口的匿名内部类对象来指定线程要执行的任务内容,再将其 给一个线程来启动。
代码分析:
对于Runnable的匿名内部类用法,可以分析出几点内容:
-
Thead 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心。
-
为了指定 run 的方法体,不得不需要 Runnable 接口的实现类
-
为了省去定义一个 Runnable 实现类的麻烦,不得不使用匿名内部类
-
必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错,而实际上,似乎只有方法体才是关键所在。
优化后:
public class Lambda01 { public static void main(String[] args) { // Lambda表达式体现的是函数式编程思想,只需要将要执行的代码放到函数中(函数就是类中 new Thread(() -> { System.out.println("Lambda表达式"); }).start(); } }
通过了解匿名内部类语法冗余,体验了Lambda表达式的使用,发现Lambda是对匿名内部类的简写。