关于JAVA8的Lambda表达式

news2024/11/20 15:50:31

1. 水在前面

        这个礼拜忽然心血来潮把Lambda表达式学习了一遍,发现这玩意跟原来想象的好像不是一个东西,写个学习心得供以后复习用。还是那句话,这篇水文不能让你完全掌握,只是用来给我自己温习用的,或者也可以作为小伙伴的学习引路,别指望能读一篇文章就学会了。

2. 关于教材

        上周为了学习Lambda还特意跑去图书馆翻了好几本书(期间还被一个8、9的小屁孩鄙视了一番),最后关于Lambda表达式的学习,还是推荐这本《JAVA 8 实战》Raoul-Gabriel Urma,Mario Fusco 著 (pdf版 wx号:zhenyeli86  添加好友请说明技术交流,5毛党)。这本书从对Lambda表达式的背景、使用、以及原理都做了解释,还是我这种水货都能看懂的解释,还为继续往后面深挖扩展。

3. 学习心得

        关于Lambda的学习最大的体会是,重要的并不是Lambda本身,而是使用Lambda的上下文。这里可能有点抽象了,但是面向对象本身就是抽象的,先买个关子,好戏在后头。

3.1. Lambda语法

(parameters) -> expression
           或者
(parameters) -> {statement}

3.2. Lambda是什么

        按我的理解,Lambda本质上其实是一个函数,一个没有声明函数名称的函数,从语法上就很明显看到有参数列表和函数主体,只不过多了个“->”符号而已。  

        至于函数的返回值也是一个很有意思的事情,Lambda的函数返回值类型,是根据调用它的上下文来确定的,或者说是根据你想用它的地方的上下文确定了你对Lambada函数的返回值的定义。又有点抽象了,我们继续往后看。

3.3. Lambda用在什么地方

        敲黑板,全文浓缩成的一句话在这里了:Lambda表达式可以作为一个函数的“参数”使用,该参数的类型是一个函数式接口!

//调用fun
fun(Lambda表达式);

//其中fun的定义如下:
void fun (函数式接口 c){
    //反正这里肯会和Lambda有关
}

        神奇不?惊讶不!上文说到Lambda本质上是一个函数,就是说,一个函数的参数居然是另一个函数!对这就是Java 8的传递函数作为参数,其实我猜本质上还是值传递,只是传递了函数的地址值。

        慢着,并不是什么函数都可以把Lambda作为参数传入的,参数类型一定是“函数式接口的实例”。那什么是函数式接口呢?函数式接口就是“只有一个抽象方法的接口”。 函数式接口不能没有抽象方法,不能没有,也不能有多个,只能是一个抽象方法,至于非抽象方法,你爱整多少个就整多少个。

3.4. 稍微做个总结

        在继续水之前,我们先来做个小小的总结:Lambda 表达式本质上是一个函数,它是可以作为一个一个参数传给另一个函数使用,而这个参数的类型是函数式接口。只有一个抽象方法的接口就叫函数式接口。

3.5. Lambda怎么用

3.5.1. 挑绿苹果小程序  

        下面我们借用一个书上的例子继续水。这个例子是这样描述的,我们要写一个小功能,这个功能是在一堆苹果里面挑出绿色的苹果给用户。好的,根据这个需求,我们写一个小程序(我觉得还是要写一个可以执行的,完整的代码才方便对照阅读)。

import java.util.ArrayList;
import java.util.List;

//定义一个小苹果
class Apple{
	private	String color;
	
	public Apple(String appleColor){
		this.color = appleColor;
	}
	
	public void setColor(String appleColor) {
		this.color = appleColor;
	}
	
	public String getColor() {
		return this.color;
	}
}

举个栗子
public class Demo {
	
	//第一版的挑苹果函数
	public static List<Apple> filterApples(List<Apple> inventory){
		List<Apple> result = new ArrayList<Apple>();
		
		for (Apple apple: inventory){
			if ("green".equals(apple.getColor())) {
				result.add(apple);
			}
		}
		
		return result;
	}
    
	//主程序
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        
		List<Apple> appleList = new ArrayList<Apple>();
		appleList.add(new Apple("red"));
		appleList.add(new Apple("green"));
		appleList.add(new Apple("red"));
		
		//获取结果
		List<Apple> ret = filterApples(appleList);
		
		System.out.println("Apples: ");
		for (Apple app: ret) {
			System.out.println( app.getColor() + " apple;"); 
		}	
	
	}	

}

        上面小程序还写得不错吧,这时候业主爸爸说我除了要挑绿色的苹果,偶尔也挑红色的苹果。作为一个有经验(被折磨多次)的搬砖工,我们立马想到,爸爸下回可能还要重量大于100克的苹果、有虫子的苹果、日本苹果甚至长得像桃子的苹果。。。

 3.5.2. 使用策略模式优化

        按以前我们除了心里问候一下,肯定也会趁机卖弄下技术,整个策略模式,反正你要怎么挑我就写个对应的策略完事。看下我们写的第二版策略模式的挑苹果小功能:

import java.util.ArrayList;
import java.util.List;

//定义一个小苹果,这次增加了重量属性应付麻烦业主
class Apple{
	private	String color;
	private int weight;
	
	public Apple(String appleColor, int appleWeight){
		this.color = appleColor;
		this.weight = appleWeight;
	}
	
	public String getColor() {
		return this.color;
	}
	
	public int getWeight() {
		return this.weight;
	}
}

//苹果挑选器,函数式接口
interface AppleSelector{
	boolean test(Apple a);
}

//绿色苹果挑选器
class GreenAppleSelector implements AppleSelector{

	@Override
	public boolean test(Apple a) {
		// TODO Auto-generated method stub
		if ("green".equals(a.getColor()))
		    return true;
		else
			return false;
	}
	
}

//红色色苹果挑选器
class RedAppleSelector implements AppleSelector{

	@Override
	public boolean test(Apple a) {
		// TODO Auto-generated method stub
		if ("red".equals(a.getColor()))
		    return true;
		else
			return false;
	}
	
}

//大苹果挑选器
class BigAppleSelector implements AppleSelector{

	@Override
	public boolean test(Apple a) {
		// TODO Auto-generated method stub
		if (100 <= a.getWeight())
		    return true;
		else
			return false;
	}
	
}

public class Demo {
	
	//第一版的挑苹果函数
	public static List<Apple> filterApples(List<Apple> inventory, AppleSelector selector){
		List<Apple> result = new ArrayList<Apple>();
		
		for (Apple apple: inventory){
			if (selector.test(apple)) {
				result.add(apple);
			}
		}
		
		return result;
	}
    
	//主程序
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        
		List<Apple> appleList = new ArrayList<Apple>();
		appleList.add(new Apple("red", 100));
		appleList.add(new Apple("green", 200));
		appleList.add(new Apple("red", 50));
		
		//挑绿苹果
		AppleSelector greenSel = new GreenAppleSelector();
		List<Apple> greenRet = filterApples(appleList, greenSel);
		
		//挑大苹果
		AppleSelector bigSel = new BigAppleSelector();
	    List<Apple> bigRet = filterApples(appleList, greenSel);
		
		System.out.println("Green Apples: ");
		for (Apple app: greenRet) {
			System.out.println( app.getColor() + " apple;"); 
		}
		
		System.out.println("Big Apples: ");
		for (Apple app: greenRet) {
			System.out.println( Integer.toString(app.getWeight()) + "g apple;"); 
		}		
		
		
	}	

}

3.5.3. Lambda版的策略模式

        写到这里小伙伴肯定要吐槽了,水了这么久连个Lambda都没见到!好了我们来看看正主怎么用。其实写道策略模式我觉得代码已经很简洁了,毕竟我只要不断的增加AppleSelector的具体实现就好了,其他的基本不用动了。但是大牛就是大牛,觉得具体实现挑选苹果策略的这段代码就的外围就出现了很多遍,不行!

因此就到了Lambda的出场时刻了!看代码:

import java.util.ArrayList;
import java.util.List;

//定义一个小苹果,这次增加了重量属性应付麻烦业主
class Apple{
	private	String color;
	private int weight;
		
	public Apple(String appleColor, int appleWeight){
		this.color = appleColor;
		this.weight = appleWeight;
	}
	
	public String getColor() {
		return this.color;
	}
	
	public int getWeight() {
		return this.weight;
	}
}

//苹果挑选器,函数式接口
interface AppleSelector{
	boolean test(Apple a);
}


public class Demo {
	
	//第一版的挑苹果函数
	public static List<Apple> filterApples(List<Apple> inventory, AppleSelector selector){
		List<Apple> result = new ArrayList<Apple>();
		
		for (Apple apple: inventory){
			if (selector.test(apple)) {
				result.add(apple);
			}
		}
		
		return result;
	}
    
	//主程序
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        
		List<Apple> appleList = new ArrayList<Apple>();
		appleList.add(new Apple("red", 100));
		appleList.add(new Apple("green", 200));
		appleList.add(new Apple("red", 50));
		
		//挑绿苹果
		List<Apple> greenRet = 
            filterApples(appleList, (Apple apple) -> "green".equals(apple.getColor()));
		
		//挑大苹果
	    List<Apple> bigRet = 
            filterApples(appleList, (Apple apple) -> 100 <= apple.getWeight());
		
		System.out.println("Green Apples: ");
		for (Apple app: greenRet) {
			System.out.println( app.getColor() + " apple;"); 
		}
		
		System.out.println("Big Apples: ");
		for (Apple app: greenRet) {
			System.out.println( Integer.toString(app.getWeight()) + "g apple;"); 
		}		
		
		
	}	

}

        原本在第二版代码中的AppleSelector接口的具体实现没了,被Lambda表达式取代了。Lambda表达式代替AppleSelector的具体实例作为参数传到了filterApples函数中。这就是Lambda的使用方法。

        这里我们需要注意一点,Lambda表达式在这个程序中的表现形式和AppleSelector的抽象函数要匹配,才不会报错

3.6. 编译器怎么识别Lambda

        水到这里,基本上讲完了Lambda表达式的使用了。我们再深挖一下,编译器是怎么工作的,专业的说法是,编译器是怎么做上下文推断的。

        让我们再次回顾一下3.3节水过的一句话:Lambda表达式可以作为一个函数的“参数”使用,该参数的类型是一个函数式接口!看看Lambda的使用:

List<Apple> bigRet = 
            filterApples(appleList, (Apple apple) -> 100 <= apple.getWeight());

        当编译器遇到了Lambda表达式,首先检查filterApples函数的参数类型,是一个AppleSelector,这是一个函数式接口,满足基本条件。再看Lambda的函数描述,参数是Apple类型,返回值是一个boolean类型,这个函数描述抽象出来就是 boolean fun(Apple a) 正好就是AppleSelector的唯一抽象方法声明。因此,编译器就是通过这样的推断,确定了Lambda表达式对应了AppleSelector的test方法。以上就是上下文推断的过程!

4. 水在最后

        呼应前文说到的,其实使用Lambda的上下文,比Lambda本身更有意思。可能我对函数式编程的内涵理解还不到位吧,我认为Lambda只是为我们提供了一种简化代码的机制,真正使代码优雅起来的是策略模式。至于说Lambda让代码变得可读性更强,我觉得见人见智吧,起码我感觉直接使用谓语似乎更容易理解。记得前东家的规范上就要求代码还是要简单易懂为优先,毕竟不知道以后是谁来读你写的代码。

        本来只是想过一遍Lambda的使用,结果书是越看越深,后面还有函数引用、函数式编程等没搞懂,还是留在下次再水吧。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DXP学习2- 绘制电气图【实验】

目录 一、实验目的 二、实验原理 1、创建一个新的项目文件。 2、新建原理图文件 3、设置原理图选项 4、放置元器件 5、其他电路元素的放置 6、对所有电路元素属性参数值的修改 三、实验设备 四、实验内容 1、绘制实验图2-1 元器件所在位置&#xff1a; 1&#xff0c;…

四、Elasticsearch 进阶

自定义目录 4.1 核心概念4.1.1 索引&#xff08;Index&#xff09;4.1.2 类型&#xff08;Type&#xff09;4.1.3 文档&#xff08;Document&#xff09;4.1.3 字段&#xff08;Field&#xff09;4.1.5 映射&#xff08;Mapping&#xff09;4.1.6 分片&#xff08;Shards&#…

基于java+springboot+vue实现的游戏账号估价交易平台(文末源码+Lw+ppt)23-555

摘 要 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对游戏账号估价交易的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自…

【计算机网络篇】数据链路层(1)数据链路层的地位,问题

文章目录 &#x1f354;数据链路层在网络体系结构中的地位&#x1f354;链路&#xff0c;数据链路&#xff0c;帧&#x1f354;数据链路层的三个重要问题&#x1f95a;封装成帧和透明传输&#x1f95a;差错检测&#x1f95a;可靠传输 &#x1f354;数据链路层在网络体系结构中的…

Tableau学习——范围-线图、倾斜图

1范围-线图&#xff08;人工接听数据&#xff09; 范围-线图&#xff1a;将整体及个体数据特征&#xff08;均值、最值等&#xff09;都展示出来了 筛选出某个员工 &#xff08;1&#xff09;创建计算字段来表示均值、最大值、最小值 &#xff08;2&#xff09;数据处理好后&…

Day61:WEB攻防-PHP反序列化原生类TIPSCVE绕过漏洞属性类型特征

知识点&#xff1a; 1、PHP-反序列化-属性类型&显示特征 2、PHP-反序列化-CVE绕过&字符串逃逸 3、PHP-反序列化-原生类生成&利用&配合 补充&#xff1a;如果在 PHP 类中没有实现某个魔术方法&#xff0c;那么该魔术方法在相应的情况下不会被自动触发。PHP 的魔…

路桥公司知识竞赛活动方案

一、参赛对象 龙建路桥股份有限公司权属企业 二、组织单位 主办单位&#xff1a;龙建路桥股份有限公司委员会 承办单位&#xff1a;黑龙江省龙建路桥第二工程有限公司委员会 三、活动时间 11月&#xff08;具体时间另行通知&#xff09; 四、活动地点 龙建松北综合经营生产中心…

基于Java中的SSM框架实现图书仓储管理系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现图书仓储管理系统演示 摘要 随着社会经济的迅速发展和科学技术的全面进步&#xff0c;计算机事业的飞速发展&#xff0c;以计算机与通信技术为基础的信息系统正处于蓬勃发展的时期&#xff0c;随着经济文化水平的显著提高&#xff0c;人们对生活质量及…

Token的详解

Token的详解 文章目录 Token的详解前言:简介:使用token&#xff1a; 前言: 为什么会用到Token&#xff0c;因为cookie和session一些自身的缺点&#xff0c;限制了一些功能的实现&#xff0c;比如&#xff1a; cookie&#xff1a;优点是节省服务器空间&#xff0c;缺点不安全。…

如何监控企业微信聊天记录内容

假如说老板可以查看到你的微信聊天记录&#xff0c;那么此时此刻的你&#xff0c;会不会瑟瑟发抖&#xff1f; 其实不用啦&#xff0c;监控企业微信聊天记录&#xff0c;也是需要员工个人同意的。 下面我介绍两种方法&#xff0c;看看你属于哪种 方法一 企业微信自带功能 …

倒计时3天!2024“智衡杯”智能感知挑战赛即将启幕!

为推动智能感知算法领域的发展&#xff0c;发掘高品质的智能感知算法&#xff0c;并促进其在实际应用中落地&#xff0c;“2024‘智衡杯’智能感知挑战赛”将于3月26日盛大启幕&#xff01; 本次大赛由全国人工智能计量技术委员会、先进技术成果长三角转化中心、北京航天计量测…

深入浅出Reactor和Proactor模式

Reactor模式和Proactor模式是两种常见的设计模式&#xff0c;用于处理事件驱动的并发编程。它们在处理IO操作时有着不同的工作方式和特点。 对于到来的IO事件&#xff08;或是其他的信号/定时事件&#xff09;&#xff0c;又有两种事件处理模式&#xff1a; Reactor模式&…

阿里云服务器“镜像”操作系统选择方法(超详细)

阿里云服务器镜像怎么选择&#xff1f;云服务器操作系统镜像分为Linux和Windows两大类&#xff0c;Linux可以选择Alibaba Cloud Linux&#xff0c;Windows可以选择Windows Server 2022数据中心版64位中文版&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说下阿里云服务器操…

CAN总线位时序

一. 简介 前面文章学习了 CAN总线协议&#xff0c;即五种数据帧格式。 I.MX6ULL 带有 CAN 控制器外设&#xff0c;叫做 FlexCAN&#xff0c;FlexCAN 符合 CAN2.0B 协议。 本文来学习一下 CAN总线的位时序。 二. CAN总线位时序 CAN 总线以帧的形式发送数据&#xff0c;但是…

Teable——强大的在线数据电子表格

公众号&#xff1a;【可乐前端】&#xff0c;每天3分钟学习一个优秀的开源项目&#xff0c;分享web面试与实战知识&#xff0c;也有全栈交流学习摸鱼群&#xff0c;期待您的关注! 每天3分钟开源 hi&#xff0c;这里是每天3分钟开源&#xff0c;很高兴又跟大家见面了&#xff0…

C++ Thread 源码 观后 自我感悟 整理

Thread的主要数据成员为_Thr 里面存储的是线程句柄和线程ID 先看看赋值运算符的移动构造 最开始判断线程的ID是否不为0 _STD就是使用std的域 如果线程ID不为0&#xff0c;那么就抛出异常 这里_New_val使用了完美转发&#xff0c;交换_Val和_New_val的值 _Thr _STD exchange(_…

BRAM底层原理详细解释(1)

目录 一、原语 二、端口简述 2.1 端口简介 2.2 SDP端口映射 三、端口信号含义补充说明 3.1 字节写使能&#xff08;Byte-Write Enable&#xff09;- WEA and WEBWE&#xff1a; 3.2 地址总线—ADDRARDADDR and ADDRBWRADDR 3.3 数据总线—DIADI, DIPADIP, DIBDI, and D…

Pycharm小妙招之Anaconda离线配环境

Pycharm小妙招之Anaconda离线配环境———如何给无法联网的电脑配python环境&#xff1f; 1. 预备工作2. 电脑1导出包2.1 环境路径2.2 压缩py38导出至U盘 3. 电脑2导入包4. 验证是否导入成功4.1 conda查看是否导入4.2 pycharm查看能否使用 1. 预备工作 WINDOWS系统电脑1(在线)…

AI跟踪报道第34期-新加坡内哥谈技术-AI新闻快报:世界即将改变

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

手撕算法-买卖股票的最佳时机(买卖一次)

描述 分析 只能买卖一次。希望在最低处买&#xff0c;最高处卖。 怎么判断最低处&#xff1f;遍历时存储已遍历的最小值。 怎么判断最高处&#xff1f;遍历时&#xff0c;比较当前位置和最小值的差&#xff0c;取较大的。 代码 class Solution {public int maxProfit(int…