JDK1.8新特性---Lambda表达式

本文详细介绍了Java 8中的Lambda表达式,包括其基本概念、语法特点、使用场景等,并通过具体示例展示了如何使用Lambda表达式简化代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接:

       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的匿名内部类用法,可以分析出几点内容:

  1. Thead 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心。

  2. 为了指定 run 的方法体,不得不需要 Runnable 接口的实现类

  3. 为了省去定义一个 Runnable 实现类的麻烦,不得不使用匿名内部类

  4. 必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错,而实际上,似乎只有方法体才是关键所在。

优化后:

 public class Lambda01 {
     public static void main(String[] args) {
         // Lambda表达式体现的是函数式编程思想,只需要将要执行的代码放到函数中(函数就是类中
         new Thread(() -> {
             System.out.println("Lambda表达式");
         }).start();
     }
 }

       通过了解匿名内部类语法冗余,体验了Lambda表达式的使用,发现Lambda是对匿名内部类的简写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值