当前位置:

首页 > 编程开发 > Java的Lambda表达式使用实例分析

Java的Lambda表达式使用实例分析

Lambada简介lambda表达式是Java8新加入的新特性,它在Java中是引入了函数式编程这一概念。那么什么是函数式编程呢?函数式编程:函数式编程是面向数学的抽象,将计算描述为一种表达式求值。我们平常所说的面向对象编程属于命令式编程,函数式编程和命令式编程的区别是:函数式编程关心数据的映射,命令式编程关系解决问题的步骤。函数式编程关系类型(代数结构)之间的关系,命令式编程关系解决问题的步骤。函数式编程的本质:函数式编程中的函数指的不是计算机中的函数,而是数学中的函数,即自变量的映射。即:一个函数的值

本文目录

    Lambada 简介

    lambda 表达式 是Java 8新加入的新特性,它在Java中是引入了函数式编程这一概念。那么什么是函数式编程呢?

    函数式编程:函数式编程是面向数学的抽象,将计算描述为一种表达式求值。

    我们平常所说的面向对象编程属于命令式编程,函数式编程和命令式编程的区别是:

    • 函数式编程关心数据的映射,命令式编程关系解决问题的步骤。

    • 函数式编程关系类型(代数结构)之间的关系,命令式编程关系解决问题的步骤。

    函数式编程的本质:

    函数式编程中的函数指的不是计算机中的函数,而是数学中的函数,即自变量的映射。即:一个函数的值仅取决于函数参数的值,不依赖其他状态。

    严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。

    函数式编程的好处:

    函数式编程的好处是主要是不可变性带来的。没有可变的状态,函数就是引用透明(Referential transparency)的和没有副作用的(No Side Effect)。

    上面这些都是一些基本的概念,但是我们平时可能接触这些方面的东西比较少,所以一开始感觉函数式编程是很难得东西。

    简单的示例

    Talk is cheap, show me the code!

    先来一个最简单的例子,可能也是介绍的最多的例子了。哈哈!

    给按钮添加监视器。

    使用匿名内部类的方式,进行添加。

    submit.addActionListener(new ActionListener() {
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
    	}
    });

    这种方式的缺点:使用了很多的模板代码,真正必要的代码,只是方法体内的代码。所以,Java 8 引入的 Lambda 表达式可以简化这种代码(当然了,也是有限制的,不是所有的匿名内部类都可以,这个后面会提到。)。

    使用 Lambda 表达式 简化代码

    submit.addActionListener((e)->{
    		JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
    });

    Lambda 表达式是一个匿名方法,将行为像数据一样进行传递。

    说明

    可以看出来,使用 Lambda 表达式简化后的代码表达变得更加清晰了,并且不用再去写繁琐的模板代码了。

    进一步简化

    参数括号和代码体的花括号也可以省略(只有一个参数时,可以省略圆括号,只有一行代码时,可以省略花括号)。

    ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);

    小结
    当使用 Lambda 表达式代替匿名内部类创建对象时,Lambda 表达式的代码块将会替代实现抽象方法的方法体,Lambda 就相当于一个匿名方法。

    Lambda 表达式的组成部分

    lambda 表达式由三部分组成:

    • 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,可以省略形参列表的圆括号。

    • 箭头(->)。英文短线和大于号。

    • 代码块。如果代码块只有一句,可以省略花括号。如果只有一条 return 语句,可以省略 return,lambda表达式会自动返回这条语句的值。

    注:
    之所以可以省略形参列表是因为 编译器 可以进行类型推断,例如:

    List dogs1 = new ArrayList();
    
    List dogs2 = new ArrayList<>();

    上面使用 菱形语法,可以省略尖括号里面的东西,这就是类型推断的作用。
    但是类型推断也不是万能的,不是所有的都可以推断出来的,所以有时候,还是要显示的添加形参类型,例如:

    先不要管这个代码的具体作用。

    BinaryOperator b = (x, y)->x*y;
    //上面这句代码无法通过编译,下面是报错信息:无法将 * 运算符作用于 java.lang.Object 类型。
    The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
    //添加参数类型,正确的代码。
    BinaryOperator b = (x, y)->x*y;

    所以,类型推断不是万能的,如果编译器无法推断,那就是我们的错误,不要过度依赖编译器。有时候,显示的添加参数类型,还是很必要的,当然了,这需要去多练习。

    函数式接口

    前面了解了,Lambda 表达式可以代替匿名内部类,进而达到简化代码,表达清晰的目的。那么使用 Lambda 表示式的前提是什么呢?-- 函数式接口

    Lambda 表达式的类型,也被称为 目标类型 (Target Type),它必须是一个函数式接口(Functional Interface)。所谓函数式接口,指的就是:只包含一个抽象方法的接口。(可以包含多个默认方法,静态方法,但必须只有一个抽象方法)。
    注:Java 8 专门提供了一个注解:@FunctionalInterface。用于标注某个接口是函数式接口,这样编译时就会检查,如果该接口含有多个抽象方法,编译器就会报错。

    上面使用 Lambda 表达式来为按钮添加了监视器,可以看出来,Lambda 表达式 代替了 new ActionListener()对象。

    所以 Lambda 的表达式就是被当成一个对象。

    例如:

    ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);

    从上面这个例子中可以看出来,Lambda 表达式实现的是匿名方法–因此它只能实现特定函数式接口中的唯一方法。
    所以 Lambda 表达式有下面两种限制:

    Lambda 表达式的目标类型必须是明确的函数式接口。 Lambda 表达式只能为函数式接口创建对象。Lambda只能实现一个方法,因此它只能为含有一个抽象方法的接口(函数式接口)创建对象。 介绍几个 Java 中重要的函数接口

    Java的Lambda表达式使用实例分析

    从这种表可以看出来,抽象方法的名字反而不是最重要的了,重要的是参数和返回值。 因为在写 Lambda 表达式的时候,也不要使用 抽象方法名了。

    上面使用几个简单的例子来说明上面接口的应用:

    测试代码

    import java.text.ParseException;
    import java.util.function.BinaryOperator;
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.function.Supplier;
    import java.util.function.UnaryOperator;
    import java.util.stream.Stream;
    
    public class Test {
    	public static void main(String[] args) throws ParseException {
    		//Lambda 表达式中的构造器引用,用于简化代码。
    		Creat c = Dog::new;
    		Dog dog = c.creat("小黑", 15);
    		System.out.println(dog.toString());
    		
    		Predicate predicate = (words)->{
    			return words.length() > 20;
    		};
    		assert predicate.test("I love you yesterday and today!") : "长度小于20";
    		assert !predicate.test("God bless you!") : "长度小于20";
    		System.out.println("------------------------");
    		
    		Consumer consumer = System.out::println;
    		consumer.accept(dog);
    		System.out.println("------------------------");
    
    		Function function = (dogObj)->{
    			return dogObj.getName();
    		};
    		System.out.println(function.apply(dog));
    		System.out.println("------------------------");
    		
    		Supplier supplier = ()->{
    			return new Dog("大黄", 4);
    		};
    		System.out.println(supplier.get());
    		
    		//一元操作符
    		UnaryOperator unaryOperation = (flag)->{
    			return !flag;
    		};
    		System.out.println(unaryOperation.apply(true));
    		
    		BinaryOperator binaryOperator = (x, y)->x*y;
    		int result = binaryOperator.apply(999, 9999);
    		System.out.println(result);
    	}
    }

    测试使用的实体类

    public class Dog {
    	private String name;
    	private int age;
    	
    	public Dog(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getAge() {
    		return age;
    	}
    
    	public void setAge(int age) {
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "Dog [name=" + name + ", age=" + age + "]";
    	}
    }

    自定义函数式接口

    @FunctionalInterface
    public interface Creat {
    	public T creat(String name, int age);
    }

    运行截图就不放了,感兴趣的可以试一下。

    说明
    我这里直接使用 Lambda 创建了对象,然后调用了这个对象的方法(就是lambda 的代码块部分),真正使用的时候,都是直接传递 Lambda 表达式的,这种方法并不推荐,但是可以让我们很好的理解为什么? 可以看出来,Lambda 表达式的作用,最后还是需要调用 重写的抽象方法的,只不过使用表达更加清晰,简化了代码。

    例如:

    		List dogs = new ArrayList<>();
    		dogs.add(new Dog("大黄", 2));
    		dogs.add(new Dog("小黑", 3));
    		dogs.add(new Dog("小哈",1));
    		//将行为像数据一样传递,使用集合的 forEach 方法来遍历集合,
    		//参数可以是一个 Lambda 表达式。
    		Consumer con = (e)->{
    			System.out.println(e);
    		};
    		dogs.forEach(con);
    		System.out.println("--------------------------\n");
    	
    		//直接传递 Lambda 表达式,更加简洁
    		dogs.forEach(e->System.out.println(e));
    		System.out.println("--------------------------\n");
    
    		//使用方法引用,进一步简化(可以看我的另一篇关于方法引用的博客)
    		dogs.forEach(System.out::println);
    		System.out.println("--------------------------\n");
    
    		//使用 Lambda 对集合进行定制排序,按照年龄排序(从小到大)。
    		dogs.sort((e1, e2)->e1.getAge()-e2.getAge());
    		dogs.forEach(System.out::println);

    可以看出来,通过使用 Lambda 表达式可以,极大的简化代码,更加方便的操作集合。值得一提的是:Lambda 表达式 和 Stream 的结合,可以拥有更加丰富的操作,这也是下一步学习的方向。

    运行截图:

    Java的Lambda表达式使用实例分析

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系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 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。