Java-Lambda表达式基本理解及使用

Lambda表达式基本理解及使用

    • 背景
    • Lambda 表达式语法
    • 函数式接口
      • 常见的函数式接口
        • Function<T, R>
        • Consumer<T>
        • Supplier<T>
        • Predicate<T>
        • UnaryOperator<T>
        • BinaryOperator<T>
    • Lambda表达式的基本使用
      • 有返回值函数式接口
      • 无返回值函数式接口
      • Lambda表达式和匿名内部类的区别
    • Lambda表达式在使用时有一些重要的注意事项

背景

​ Lambda 表达式是Java 8引入的一个重要特性,它旨在简化函数对象的创建过程,使得Java语言更加简洁、功能更加强大,尤其是在处理集合、流和并发编程中表现突出。Lambda允许将功能作为方法参数或将其赋值给变量,这在函数式编程范式中尤为重要,它促进了行为参数化,即代码可以根据传入的行为(函数)动态变化。

Lambda 表达式(Lambda expression)可以看作是一个匿名函数

Lambda 表达式语法

Lambda表达式的语法结构一般为:

(parameters) -> expression
或者
(parameters) -> {
    statements;
}
  • parameters一个或多个参数,参数之间用逗号分隔,无参数可留空
  • ->:箭头符号,表示“指向”,连接参数列表和主体
  • expression单个表达式当Lambda体只有一条语句时可直接使用
  • { statements; }当Lambda体包含多条语句时,需要用花括号包裹起来,并明确指定返回值(如果有的话)
// 1. 不需要参数,返回值为 2
() -> 2
// 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表达式的基础Java 8引入了@FunctionalInterface注解来标记这类接口,但即使没有这个注解只要满足条件的接口也可以作为Lambda的目标类型

常见的函数式接口

  • `Function<T, R>``:接收一个T类型的输入,产生一个R类型的输出。
  • Consumer<T>:接收一个输入参数并执行操作,无返回值。
  • Supplier<T>:无参,产生一个结果。
  • Predicate<T>:接收一个输入参数,返回一个布尔值。
  • UnaryOperator<T>:一元操作符,输入和输出类型相同。
  • BinaryOperator<T>:二元操作符,输入和输出类型相同。
Function<T, R>

Function接口代表一个接受一个输入参数产生一个输出的函数。它有一个抽象方法R apply(T t)
示例:将字符串转换为大写。

Function<String, String> toUpperCase = String::toUpperCase;
String result = toUpperCase.apply("hello world");
System.out.println(result); // 输出: HELLO WORLD
Consumer

Consumer接口代表一个接收单个输入参数并且无返回的操作。它有一个抽象方法void accept(T t)
示例:打印字符串。

    Consumer<String> printer = System.out::println;
    printer.accept("This is an example of using Consumer."); // 直接打印字符串
Supplier

Supplier接口不接受任何参数,但它可以提供一个结果。它有一个抽象方法T get()
示例:生成随机数。

    Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
    System.out.println(randomSupplier.get()); // 输出一个0到99之间的随机数
Predicate

Predicate接口代表一个布尔值条件,它接受一个输入参数并返回一个布尔值结果。它有一个抽象方法boolean test(T t)
示例:检查字符串是否为空。

    Predicate<String> isEmpty = String::isEmpty;
    System.out.println(isEmpty.test("")); // 输出: true
    System.out.println(isEmpty.test("not empty")); // 输出: false
UnaryOperator

UnaryOperator是一个特殊的Function,它的输入和输出类型相同
示例:自增操作。

    UnaryOperator<Integer> increment = x -> x + 1;
    System.out.println(increment.apply(10)); // 输出: 11
BinaryOperator

BinaryOperator也是一个特殊的Function用于两个相同类型的操作数,并产生相同类型的结果
示例:两个整数相加。

    BinaryOperator<Integer> add = Integer::sum;
    System.out.println(add.apply(5, 3)); // 输出: 8

Lambda表达式的基本使用

有返回值函数式接口

    //无参
    @FunctionalInterface
    interface RandomSupplier {
        int getRandom();
    }
    //一个参数
    @FunctionalInterface
    interface SquareFunction {
        int square(int number);
    }
    //两个参数
    @FunctionalInterface
    interface MaxFinder {
        int findMax(int a, int b);
    }
    public static void main(String[] args) {
        RandomSupplier r = () -> new Random().nextInt(100);
        System.out.println(r.getRandom()); // 输出一个0到99的随机数

        SquareFunction square = number -> number * number;
        System.out.println(square.square(5)); // 输出: 25

        MaxFinder maxFinder = (a, b) -> Math.max(a, b);
        System.out.println(maxFinder.findMax(10, 20)); // 输出: 20
    }

在这里插入图片描述

无返回值函数式接口

    //无参
    @FunctionalInterface
    interface NoReturnAction {
        void performAction();
    }
    //一个参数
    @FunctionalInterface
    interface SingleParamAction<T> {
        void execute(T param);
    }
    //两个参数
    @FunctionalInterface
    interface DualParamAction<T, U> {
        void apply(T first, U second);
    }
    public static void main(String[] args) {
        NoReturnAction greetUser = () -> System.out.println("欢迎来到我们的平台!");
        greetUser.performAction(); // 执行动作

        SingleParamAction<String> logMessage = message -> System.out.println("记录信息: " + message);
        logMessage.execute("用户登录事件"); // 执行动作并传递参数

        DualParamAction<String, Integer> processOrder = (item, quantity) ->
                System.out.println("处理订单: " + item + ", 数量: " + quantity);
        processOrder.apply("苹果", 10); // 执行动作并传递两个参数
    }

在这里插入图片描述

Lambda表达式和匿名内部类的区别

1.所需类型

  • Lambda表达式:适用于函数式接口(即只有一个抽象方法的接口)。Lambda表达式能够直接提供这个方法的实现
  • 匿名内部类:可以用于任何类型的类或接口,不限于函数式接口。对于有多个抽象方法的接口或抽象类,必须实现所有抽象方法。

2.使用限制不同

  • Lambda表达式:由于其简洁性,通常用于简单操作。不支持复杂的多行语句或声明额外变量(除非在局部作用域内)。
  • 匿名内部类:提供了更多的灵活性,可以在其中定义变量、实现复杂的逻辑,甚至包含构造方法

3.使用原理不同

  • Lambda表达式:编译器会自动推断出目标类型,并将其转换为一个函数式接口的实例。Lambda表达式本质上是一种编译器的语法糖,使得代码更加简洁。 编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
  • 匿名内部类:在编译时会创建一个新的类(没有类名),这个类继承或实现了指定的父类或接口并在运行时实例化这个新类的对象
public class LambdaVsAnonymous {

    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // 使用Lambda表达式遍历列表并打印元素
        names.forEach(name -> System.out.println(name));
        System.out.println("------------------------------");
        // 使用匿名内部类遍历列表并打印元素
        names.forEach(new Consumer<String>() {
            @Override
            public void accept(String name) {
                System.out.println(name);
            }
        });

        // 使用匿名内部类实例化抽象类Shape
        Shape shape = new Shape() {
            @Override
            void draw() {
                System.out.println("绘制形状");
            }
        };
        // 调用抽象方法
        shape.draw();
    }
}
abstract class Shape {
    abstract void draw();
}

在这里插入图片描述

Lambda表达式在使用时有一些重要的注意事项

  • 类型推断:Java编译器能够根据上下文自动推断Lambda表达式的类型,因此通常不需要显式声明类型。但是,理解其背后的目标类型(通常是函数式接口)对于正确使用Lambda至关重要
  • 函数式接口:Lambda表达式只能用于实现具有一个抽象方法的接口(称为函数式接口)。如果试图将Lambda赋值给非函数式接口,将会导致编译错误
  • 简洁性:Lambda表达式旨在简化代码,通常用于简短的操作。复杂的逻辑应该考虑使用传统方法或分离成多个步骤。
  • 访问外部变量:Lambda表达式可以访问其所在范围内的最终变量和有效final变量这意味着外部变量在Lambda中要么是不可变的,要么在Lambda创建前已经赋值且不再改变
  • 捕获变量:在Java 8中,Lambda表达式通过值捕获外部变量,而在Java 9及以上版本中,也可以通过var关键字声明局部变量,但同样受到不可变性的限制。
  • 并行处理:当Lambda表达式用于并行流(例如parallelStream())时,需要特别注意变量的并发访问问题,避免数据竞争和一致性问题。
  • 异常处理:Lambda表达式中可以抛出和处理异常,但需要注意异常传播的规则,特别是在函数式接口方法声明了受检异常时。
  • 可序列化:如果Lambda表达式所实现的函数式接口是可序列化的(如Serializable),那么Lambda表达式本身也必须是可序列化的。这可能影响到捕获的外部变量是否可序列化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/699998.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

excel两个数据表格,怎样实现筛选的联动?

如图&#xff0c;想要通过处理器或者像素条件进行筛选&#xff0c;形成一个右边图2的对比表&#xff0c;如何实现实现联动显示呢&#xff1f; 这个在excel里可以借用数据透视表切片器来完成。步骤如下&#xff1a; 1.添加表 选中数据区域中任意一个单元格&#xff0c;点击 插…

Java--数组的声明和创建

1.首先必须声明数组变量&#xff0c;才能在程序中使用数组。下面是声明数组变量的语法&#xff1a; 首选的方法为Java的方法&#xff0c;另一种方法是由C或C引入过来的&#xff0c;日常使用首选Java的方法 dataType[] arrayRefVar; //首选的方法 或 dataType arrayRefVar[]…

多年不见,我美少女又回来了!

各位&#xff0c;可能很多人都不记得我了&#xff0c;上学的时候喜欢记学习笔记&#xff0c;好多学弟学妹们经常来我的博客看笔记&#xff0c;对于学习也有帮助。 时过境迁&#xff0c;生活中的琐事和繁忙的工作&#xff0c;真的自顾不暇… 还记得之前说要转型给大家分享内容运…

【算法与数据结构】【数组篇】【题6-题10】

系列文章 本人系列文章-CSDN博客https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5502 1.数组基本知识点 1.1概念 数组就是一个集合。数组会用一些名为索引的数字来标识每项数据在数组中的位置&#xff0c;且在大多数编程语言中&…

嵌入式系统概述

嵌入式系统是为了特定应用而专门构建的计算机系统&#xff0c;其嵌入式软件的架构设计与嵌入式系统硬件组成紧密相关。 1.嵌入式系统发展历程 嵌入式系统的发展大致经历了五个阶段&#xff1a; 第一阶段&#xff1a;单片微型计算机&#xff08;SCM&#xff09;&#xff0c;及…

鸿蒙开发文件管理:【@ohos.fileio (文件管理)】

文件管理 该模块提供文件存储管理能力&#xff0c;包括文件基本管理、文件目录管理、文件信息统计、文件流式读写等常用功能。 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 impor…

我要成为算法高手-双指针篇

目录 什么是双指针?问题1&#xff1a;移动零问题2&#xff1a;复写零问题3&#xff1a;快乐数问题4&#xff1a;盛最多水的容器问题5&#xff1a;有效三角形个数问题6&#xff1a;查找总价格和为目标值的两个商品(两数之和)问题7&#xff1a;三数之和问题8&#xff1a;四数之和…

【Affine / Perspective Transformation】

文章目录 仿射变换介绍仿射变换 python 实现——cv2.warpAffine透视变换透视变换 python 实现——cv2.warpPerspective牛刀小试各类变换的区别与联系仿射变换和单应性矩阵透视变换和单应性矩阵 仿射变换介绍 仿射变换&#xff08;Affine Transformation&#xff09;&#xff0…

鸿蒙轻内核A核源码分析系列四(2) 虚拟内存

本文我们来熟悉下OpenHarmony鸿蒙轻内核提供的虚拟内存&#xff08;Virtual memory&#xff09;管理模块。 本文中所涉及的源码&#xff0c;以OpenHarmony LiteOS-A内核为例&#xff0c;均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_a 获取。如果涉及开发板…

基于微信小程序的“最多跑一次”警务信息管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

Linux用户,用户组,所有者权限分配,sftp用户权限分配

注意以下命令执行需要在root用户下执行 tenant命令切换至root命令 sudo -do root 删除用户信息 1.不删除用户主目录 userdel user_name 2.删除用户主目录 userdel -r user_name usermod命令修改用户账户权限 更改用户名 sudo usermod -l newusername oldusername 更…

亚马逊竞品分析之如何查找竞品

初选之后,要对产品进行竞品分析,查找竞品的方法: 1.Best Seller榜单查找 进入到该类目的BS榜单去找跟你选中的产品的竞品 看完BS榜单会找出一部分竞品 这个找相似也可以点击,是插件的一个以图搜图的功能,不过有的时候不太好使,某些同款产品可能搜不到。 Edge浏览器搭…

The First项目报告:Stargate Finance重塑跨链金融的未来

Stargate Finance是一个基于LayerZero协议的去中心化金融平台&#xff0c;自2022年3月由LayerZero Labs创建以来&#xff0c;一直致力于为不同区块链之间的资产转移提供高效、低成本的解决方案。凭借其独特的跨链技术和丰富的DeFi服务&#xff0c;Stargate Finance已成为连接不…

Vue3相关语法内容,组件传值,事件监听,具名插槽。

1、Vue3相关语法内容 赋值语句(ref、reactive系列)组件传值(父子&#xff0c;子父)watch&#xff0c;watchEffect监听slot具名插槽 1、赋值语法&#xff08;ref&#xff0c;reactive&#xff09; 1.1、ref 、isRef、 shallowRef、triggerRef、customRef 支持所有的类型&…

视觉SLAM14精讲——相机与图像3.2

视觉SLAM14精讲 三维空间刚体运动1.0三维空间刚体运动1.1三维空间刚体运动1.2李群与李代数2.1相机与图像3.1 视觉SLAM14精讲——相机与图像3.2 视觉SLAM14精讲畸变有关重投影误差缩放实际使用 畸变 相机畸变是相机镜头光学缺陷所致的缺陷&#xff0c; 在光学领域这种问题是没…

【学习笔记】使用gcc编译程序并检查依赖的库

编译 gcc echo.c -o app -lfcgi-o app&#xff1a;指定编译后的输出文件名为 app。 -lfcgi&#xff1a;告诉编译器链接 FastCGI 库。 检查 ldd appldd 是一个在 Unix 和类 Unix 系统中用来打印一个已编译的程序所依赖的共享库列表的命令。当你运行 ldd app 命令时&#xff0…

机器人建模、运动学与动力学仿真分析(importrobot,loadrobot,smimport)

机器人建模、运动学与动力学仿真分析是机器人设计和开发过程中的关键步骤。 一、机器人建模 机器人建模是描述机器人物理结构和运动特性的过程。其中&#xff0c;URDF&#xff08;Unified Robot Description Format&#xff09;是一种常用的机器人模型描述方法。通过URDF&…

【CTS】android CTS测试

android CTS测试 1.硬件准备2. 软件准备3. 下载 CTS3.1 cts3.2 解压 CTS 包&#xff1a; 4 配置adb fastboot5 检查 Java 版本6 安装aapt26.1 下载并安装 Android SDK6.2 找到 aapt2 工具6.3 配置环境变量 7. 准备测试设备8. 运行 CTS 测试8.1 启动 CTS&#xff1a; 9. 查看测试…

vue-cli 快速入门

vue-cli &#xff08;目前向Vite发展&#xff09; 介绍&#xff1a;Vue-cli 是Vue官方提供一个脚手架&#xff0c;用于快速生成一个Vue的项目模板。 Vue-cli提供了如下功能&#xff1a; 统一的目录结构 本地调试 热部署 单元测试 集成打包上线 依赖环境&#xff1a;NodeJ…

第8章 函数

第8章 函数 8.1 定义函数8.1.1 向函数传递信息8.1.2 实参和形参 8.2 传递实参8.2.1 位置实参8.2.2 关键字实参8.2.3 默认值 8.3 返回值8.3.1 返回简单值8.3.2 让实参变成可选的8.3.3 返回字典8.3.4 结合使用函数和 while 循环 8.4 传递列表8.4.1 在函数中修改列表8.4.2 禁止函数…