当前位置:

首页 > 编程开发 > java8中lambda表达式的语法是什么

java8中lambda表达式的语法是什么

Lambda表达式类似匿名函数,简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和方法名。Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。Lambda表达式的语法(parameters)->expression或(parameters)->{statements;}参数说明:可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。可选的大括号:如果主体包含了一个语句,就不需要使用大括号。可选的返回关键

本文目录

      Lambda表达式类似匿名函数,简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和方法名。

      Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

      Lambda表达式的语法

      (parameters) -> expression
      或
      (parameters) -> { statements; }

      参数说明:

      • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

      • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

      • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

      • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值。

      举例说明:

      // 1. 不需要参数,返回值为5
      () -> 5
      
      // 2. 接收一个参数(数字类型),返回其2倍的值
      x -> 2 * x
      
      // 3. 接受2个参数(数字),并返回他们的差值
      (x, y) -> x – y
      
      // 4. 接收2个int型整数,返回他们的和
      (int x, int y) -> x + y
      
      // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
      (String s) -> System.out.print(s)

      Lambda表达式作用域

      lambda表达式中可以引用任何外部的变量或者常量。但是对这些外部的变量是有要求的:它们必须是Effectively final的。

      局部内部类和匿名内部类访问的局部变量必须由final修饰,java8开始,可以不加final修饰符,由系统默认添加。java将这个功能称为:Effectively final功能。

      方法引用

      指向静态方法的方法引用

      Function function1 = Integer::parseInt; // 等价于下面
      Function function2 = (String i) -> Integer.parseInt(i);

      指向任意类型实例方法的方法引用

      Function function3 = String::toLowerCase; // 等价于下面
      Function function4 = (String i) -> i.toLowerCase();
      
      BiFunction biFunction = (String s, Integer i) -> s.substring(i);
      BiFunction biFunction2 = String::substring;

      指向现有对象的实例方法的方法引用

      String str = "hello";
      Supplier supplier = () -> str.length();
      Supplier supplier2 = str::length;
      
      Function function5 = (Integer i) -> str.substring(i);
      Function function6 = str::substring;

      构造方法引用

      package com.morris.java8.lamdba;
      
      import java.util.function.BiFunction;
      import java.util.function.Function;
      import java.util.function.Supplier;
      
      public class MethodReferenceExample {
      
          public static void main(String[] args) {
      
              // 构造函数引用
              Supplier stringSupplier = () -> new String();
              Supplier stringSupplier2 = String::new;
      
              Function stringFunction = (String s)->new String(s);
              Function stringFunction2 = String::new;
      
              BiFunction stringBiFunction = (Runnable r, String b)-> new Thread(r, b);
              BiFunction stringBiFunction2 = Thread::new;
      
              ThreeFunction threeFunction = (ThreadGroup g, Runnable r, String b)-> new Thread(g, r, b);
              ThreeFunction threeFunction2 = Thread::new;
      
          }
      
          interface ThreeFunction {
              D triple(A a, B b, C c);
          }
      
      }

      lambda与匿名内部类

      从表面上看到Lambda表达式似乎只是为了简化匿名内部类书写,这看起来仅仅通过语法糖在编译阶段把所有的Lambda表达式替换成匿名内部类就可以了。但实际并非如此。在JVM层面,Lambda表达式和匿名内部类有着明显的差别。

      匿名内部类

      匿名内部类仍然是一个类,只是不需要程序员显示指定类名,编译器会自动为该类取名。

      public class AnonymousClassDemo {
      
          public static void main(String[] args) {
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("this is an Anonymous class demo");
                  }
              });
          }
      }

      因此上面的代码,编译之后将会产生两个class文件:

      AnonymousClassDemo.class
      AnonymousClassDemo$1.class

      进一步分析主类AnonymousClassDemo.class的字节码,可发现其创建了匿名内部类的对象:

      $ javap -v -p AnonymousClassDemo.class
      ...
        public static void main(java.lang.String[]);
          descriptor: ([Ljava/lang/String;)V
          flags: ACC_PUBLIC, ACC_STATIC
          Code:
            stack=4, locals=1, args_size=1
               0: new           #2                  // class java/lang/Thread
               3: dup
               4: new           #3                  // class AnonymousClassDemo$1 创建匿名内部类
               7: dup
               8: invokespecial #4                  // Method AnonymousClassDemo$1."":()V
              11: invokespecial #5                  // Method java/lang/Thread."":(Ljava/lang/Runnable;)V
              14: pop
              15: return
            LineNumberTable:
              line 5: 0
              line 11: 15
      }
      SourceFile: "AnonymousClassDemo.java"
      InnerClasses:
           static #3; //class AnonymousClassDemo$1

      lambda表达式

      Lambda表达式通过invokedynamic指令实现,不会产生新的类。

      public class LambdaDemo {
          public static void main(String[] args) {
              new Thread(()-> System.out.println("this is a lambda demo"));
          }
      }

      上面的代码编译之后只有一个class文件:

      LambdaDemo.class

      通过javap查看LambdaDemo.class的字节码,我们更能看出Lambda表达式内部表示的不同。

      $ javap -v -p LambdaDemo.class
      ...
        public static void main(java.lang.String[]);
          descriptor: ([Ljava/lang/String;)V
          flags: ACC_PUBLIC, ACC_STATIC
          Code:
            stack=3, locals=1, args_size=1
               0: new           #2                  // class java/lang/Thread
               3: dup
               4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable; 使用invokedynamic指令调用
               9: invokespecial #4                  // Method java/lang/Thread."":(Ljava/lang/Runnable;)V
              12: pop
              13: return
            LineNumberTable:
              line 4: 0
              line 5: 13
      
        private static void lambda$main$0(); // Lambda表达式被封装成主类的私有方法
          descriptor: ()V
          flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
          Code:
            stack=2, locals=0, args_size=0
               0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
               3: ldc           #6                  // String this is a lambda demo
               5: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
               8: return
            LineNumberTable:
              line 4: 0
      }
      SourceFile: "LambdaDemo.java"
      InnerClasses:
           public static final #51= #50 of #54; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
      BootstrapMethods:
        0: #22 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
          Method arguments:
            #23 ()V
            #24 invokestatic LambdaDemo.lambda$main$0:()V
            #23 ()V

      反编译之后我们发现Lambda表达式被封装成了主类的一个私有方法,并通过invokedynamic指令进行调用。

      既然Lambda表达式不是内部类的简写,那么Lambda内部的this引用也就跟内部类对象没什么关系了。在Lambda表达式中this的意义跟在表达式外部完全一样。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java
      相关文章 更多
      Java单元测试入门:使用JUnit 5编写第一个测试用例
      Java单元测试入门:使用JUnit 5编写第一个测试用例

      面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

      Java变量未初始化报错解析:局部变量与成员变量默认值差异
      Java变量未初始化报错解析:局部变量与成员变量默认值差异

      深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

      详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

      Java中final与static final常量的定义及初始化规则
      Java中final与static final常量的定义及初始化规则

      深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

      Java抽象方法与接口方法的区别及选型建议
      Java抽象方法与接口方法的区别及选型建议

      深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

      Java实现HTTP请求与JSON解析的完整代码示例
      Java实现HTTP请求与JSON解析的完整代码示例

      本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

      Java递归出现StackOverflowError的原因分析及栈空间优化方案
      Java递归出现StackOverflowError的原因分析及栈空间优化方案

      详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

      Java空指针异常(NPE)的代码排查与修复实例
      Java空指针异常(NPE)的代码排查与修复实例

      针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

      selenium上传文件教程本地文件路径设置方法
      selenium上传文件教程本地文件路径设置方法

      深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

      针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。