Lambda表达式超详解

目录

背景

Lambda表达式的用法

函数式接口

Lambda表达式的基本使用

语法精简

 变量捕获

匿名内部类

匿名内部类中的变量捕获

Lambda的变量捕获

Lambda表达式在类集中的使用

Collection接口

 List接口

Map接口

总结


背景

Lambda表达式是Java SE 8中的一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口.lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块).Lambda表达式基于数学中的λ演算得名,也可以称为闭包.

Lambda表达式的用法

基本语法:(parameters)->expression或(parameters)->{statements;}

Lambda表达式由三个部分组成:

1.parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数.这里的参数类型可以明确声明也可不声明而由JVM隐含的判断.另外当只有一个推断类型时可以省略掉圆括号.

2.->:可理解为被用于的意思

3.方法体:可以是表达式也可以代码块,是在函数式接口里方法的实现.代码块可返回一个值或什么都不返回,这里的代码块等同于方法中的方法体,如果是表达式,可以返回一个值或者什么都不返回.

举个栗子:

//1.不需要参数,返回值为2

()->2

//2.接收一个参数(数字类型),返回其两倍的值

x -> 2 * x

//3.接收两个参数(数字),并返回它们的和

(x, y) -> x + y

//4.接收2个int型整数,返回它们的乘积

(int x, int y) -> x * y

//5.接收一个string对象,并在控制台打印,不返回任何值(看起来是返回void)

(String s) -> System.out.println(s)

函数式接口

要了解Lambda表达式,首先需要了解什么是函数式接口,函数接口定义:一个接口有且只有一个抽象方法.

注意:

1.如果一个接口有且只有一个抽象方法,那么该接口就是函数式接口

2.如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的.所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解.加上就会自动进行检测的.

定义方式:

@FunctionalInterface
interface NoParameterNoReturn {//注意:只能有一个方法void test();
}

但是这种方法也是可以的:

@FunctionalInterface
interface NoParameterNoReturn {void test();default void test2() {System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");}
}

Lambda表达式的基本使用

首先,我们事先准备好几个接口:

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {void test();
}//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a, int b);
}//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {int test();
}//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {int test(int a, int b);
}

我们在上面提到过,Lambda可以理解为:Lambda就是匿名内部类的简化,实际上是创建了一个类,实现了接口,重写了接口的方法.

没有使用lambda表达式时的调用方式:

NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){@Overridepublic void test() {System.out.println("hello");}
};

具体使用见以下代码:

public class TestDemo {public static void main(String[] args) {NoParameterNoReturn noParameterNoReturn = () -> {System.out.println("无参数无返回值");};noParameterNoReturn.test();OneParameterNoReturn oneParameterNoReturn = (int a) -> {System.out.println("一个参数一个返回值: " + a);};oneParameterNoReturn.test(10);MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {System.out.println("多个参数无返回值: " + a + " " + b);};moreParameterNoReturn.test(20, 30);NoParameterReturn noParameterReturn = () -> {System.out.println("有返回值无参数! ");return 40;};//接收函数的返回值int ret = noParameterReturn.test();System.out.println(ret);OneParameterReturn oneParameterReturn = (int a) -> {System.out.println("有返回值且有一个参数");return a;};ret = oneParameterReturn.test(50);System.out.println(ret);MoreParameterReturn moreParameterReturn = (int a, int b) -> {System.out.println("有返回值且有多个参数");return a + b;};ret = moreParameterReturn.test(70, 60);System.out.println(ret);}
}

语法精简

1.参数类型可以省略,如果需要省略,每个参数的类型都需要省略

2.参数的小括号里面只有一个参数,那么小括号可以省略.

3.如果方法体中只有一句代码,那么大括号可以省略.

4.如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字.

 示例代码:

public static void main(String[] args) {MoreParameterNoReturn moreParameterNoReturn = (a, b) -> {System.out.println("无返回值多个参数,省略参数类型: " + a + " " + b);};moreParameterNoReturn.test(20, 30);OneParameterNoReturn oneParameterNoReturn = a -> {System.out.println("无参数一个返回值, 小括号可以省略: " + a);};oneParameterNoReturn.test(10);NoParameterNoReturn noParameterNoReturn = () -> System.out.println("无参数无返回值,方法体中只有一行代码");noParameterNoReturn.test();//方法体中只有一条语句,且是return语句NoParameterReturn noParameterReturn = () -> 40;int ret = noParameterReturn.test();System.out.println(ret);}

 变量捕获

Lambda表达式中存在标量捕获,了解变量捕获之后,我们才能更好地理解Lambda表达式的作用域.

Java当中的匿名类中,会存在变量捕获.

匿名内部类

匿名内部类就是没有名字的内部类.我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用即可,那么下面来简单看看匿名内部类的使用就好了.

举个简单的例子:

class Test {public void func() {System.out.println("func()");}
}public class TestDemo {public static void main(String[] args) {Test t = new Test() {@Overridepublic void func() {System.out.println("我是匿名内部类,且重写了func这个方法! ");}};t.func();}
}

在上述代码当中的main函数当中,我们看到的就是一个匿名内部类的简单使用.

匿名内部类中的变量捕获

在匿名内部类中,在使用内外的变量时,有两个条件:(要么是常量,要么是未修改的变量

来看一下代码:

public class TestDemo1 {public static void main(String[] args) {int a = 100;Test t1 = new Test() {@Overridepublic void func() {System.out.println("我是匿名内部类,且重写了func这个方法!");System.out.println("我是捕获到变量a==" + a + "我是一个常量, 或者是一个没有改变过值的变量! ");}};t1.func();}
}

在上述代码当中变量a就是捕获的变量.这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前没有修改.如下代码就是错误的代码.

 

public class TestDemo2 {public static void main(String[] args) {int a = 100;Test t2 = new Test() {@Overridepublic void func() {a = 99;System.out.println("我是匿名内部类,且重写了func这个方法!");System.out.println("我是捕获到变量a==" + a + "我是一个常量, 或者是一个没有改变过值的变量! ");}};}
}

该代码直接编译报错.

Lambda的变量捕获

lambda其实就是可以认为是匿名内部类的实现

在lambda表达式中也可以进行变量的捕获,具体我们看一下代码.

@FunctionalInterface
interface NoParameterNoReturn {void test();
}public class TestDemo3 {public static void main(String[] args) {int a = 10;NoParameterNoReturn noParameterNoReturn = () -> {//a = 99;errorSystem.out.println("捕获变量" + a);};noParameterNoReturn.test();}
}

Lambda表达式在类集中的使用

 为了能够让Lambda和java的集合类更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接.

对应的接口新增的方法
Collectionremovelf() spliterator() stream() parallelStream() forEach()
ListreplaceAll() sort()
Map

getOrDefault() forEach() replaceAll() putlfAbsent() remove() replace()

computeIfAbsent() computeIfPresent() compute() merge()

以上方法的作用可自行查看javaapi.

Collection接口

forEach()方法演示

该方法在接口Iterable当中,原型如下:

default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for(T t : this) {action.accept(t);}
}

该方法表示:对容器中的每个元素执行action指定动作.

public class TestDemo4 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");list.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {//简单遍历集合中元素System.out.println(s + " ");}});}
}

 输出结果:

我们可以修改为如下代码:

public class TestDemo4 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");//表示调用一个,不带有参数的方法,其执行花括号内的语句,为原来的函数体内容.list.forEach(s -> System.out.println(s));}
}

 List接口

sort()方法的演示

sort()方法源码:该方法根据c指定的比较规则对容器元素进行排序.

public void sort(Comparator<? super E> c) {final int expectedModCount = modCount;Arrays.sort((E[]) elementData, 0, size, c);if(modCount != expectedModCount) {throw new ConcurrentModificationExpection();}modCount++;
}

使用示例:

public class TestDemo5 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("hello");list.add("bit");list.add("lambda");System.out.println(list);list.sort(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {//这里比较长度return o1.length() - o2.length();}});System.out.println(list);}
}

输出结果:

 

修改为lambda表达式:

public class TestDemo5 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("hello");list.add("bit");list.add("lambda");System.out.println(list);//调用带有两个参数的方法,且返回长度的差值list.sort((str1, str2) -> str1.length() - str2.length());System.out.println(list);}
}

Map接口

HashMap的forEach()

该方法的原型如下:

default void forEach(BiConsumer<? super K, ? super V> action) {Objects.requireNonNull(action);for(Map.Entry<K, V> entry : entrySet()) {K k;V v;try {k = entry.getKey();v = entry.getValue();} catch {//this usually means the entry is no longer in the map.throw new ConcurrentModificationExpection(ise);}action.accept(k, v);}
}

作用是对Map中的每个映射执行action指定操作.

代码示例:

public class TestDemo6 {public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "bit");map.put(3, "hello");map.put(4, "lambda");map.forEach(new BiConsumer<Integer, String>() {@Overridepublic void accept(Integer integer, String s) {System.out.println(integer + " = " + s);}});}
}

输出结果:

 

使用lambda表达式之后的代码:

public class TestDemo6 {public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "bit");map.put(3, "hello");map.put(4, "lambda");map.forEach((k, v) -> System.out.println(k + " = " + v));}
}

总结

lambda表达式的优点很明显,在代码层次上来说,使代码变得非常地简洁.缺点也不明显,代码不易读.

优点:

1.代码简洁,开发迅速

2.方便函数式编程

3.非常容易进行并行运算

4.Java引入了Lambda,改善了集合操作 

缺点:

1.代码可读性变差

2.在非并行计算中,很多计算未有传统的for性能要高

3.不容易进行调试 

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

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

相关文章

【ArcGIS微课1000例】0043:ArcGIS绘制国界线的3种方法

本文讲解ArcGIS绘制国界线的3种方法。 文章目录 1. 直接修改国界线符号2. 缓冲区工具3. 制图表达1. 直接修改国界线符号 直接修改国界线/省界线的符号。点击“线要素”出现符号选择器,点击【编辑符号】按钮,编辑成下面右图的形式。缺点:只能在边界一侧出现缓冲样式,如下面…

C# 获取系统已安装的.NET版本

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;唐宋元明清原文地址&#xff1a; https://www.cnblogs.com/kybs0/p/16478587.htmlC# 获取系统已安装的.NET版本获取系统已安装的.NET版本&#xff0c;来确定当前应用可运行的环境。获取系…

.NET 6 Minimal API 的经验分享

Minimal API 是 .NET 6 提供的最新功能 &#xff0c; 对比传统的 ASP.NET Core Web API 方式更加直接 , 你可以用几行代码编写好 REST API 。 没有了祖传的 Startup.cs 和 Controller &#xff0c;通过简单的代码就可以完成 API 的开发。在第二阶段的 .NET 挑战赛中就以 .NET 6…

JavaWeb之Filter过滤器

原本计划这一篇来总结JSP&#xff0c;由于JSP的内容比较多&#xff0c;又想着晚上跑跑步减减肥&#xff0c;所以今天先介绍Filter以及它的使用举例&#xff0c;这样的话还有些时间可以锻炼锻炼。言归正传&#xff0c;过滤器从字面理解她的话有拦网、过滤的功能&#xff0c;可以…

【ArcGIS微课1000例】0054:尺寸注记的创建与编辑

尺寸注记要素是一种特殊类型的文本,用于显示地图上的长度或距离,可以创建各种形状的尺寸注记要素,如对齐、简单对齐、水平线状、垂直线状和旋转线状等。 文章目录 一、创建尺寸注记1. 直接创建尺寸注记要素2. 通过已有尺寸注记要素创建二、编辑尺寸注记1. 删除尺寸注记要素2…

利用python实现批量查询ip地址归属地址

今天需要查询nginx访问的客户端ip是否和调度一样&#xff01;先是用shell把文件中的ip截取出来&#xff1a; python脚本如下&#xff1a;&#xff08;哈哈&#xff0c;新手写的很草率&#xff09;#!/usr/bin/env#-- coding: utf-8 - import jsonimport urllibimport socketimpo…

基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

系列文章基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客&#xff1f;基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目基于.NetCore开发博客项目 StarBlog - (3) 模型设计基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入基于.N…

堪比JMeter的.Net压测工具 - Crank 入门篇

1. 前言 Crank 是.NET 团队用来运行基准测试的基准测试基础架构&#xff0c;包括&#xff08;但不限于&#xff09;来自TechEmpower Web 框架基准测试的场景,是2021年.NET Conf 大会上介绍的一项新的项目&#xff0c;其前身是Benchmarks。 Crank目标之一是为开发人员提供一种工…

【GlobalMapper精品教程】016:按照指定字段批量生成不同用地类型的矢量图层

Globalmapper中可以很方便的根据指定的字段,对矢量数据进行批量提取,生成不同类型的多个矢量数据,本文以土地利用现状数据为例,基于DLMC,提取出不同用地类型的矢量图层。 参考阅读:【ArcGIS遇上Python】ArcGIS Python按照指定字段批量筛选不同类型的图斑(以土地利用数据…

javascript闭包—围观大神如何解释闭包

闭包的概念已经出来很长时间了&#xff0c;网上资源一大把&#xff0c;本着拿来主意的方法来看看。 这一篇文章 学习Javascript闭包&#xff08;Closure&#xff09; 是大神阮一峰的博文&#xff0c;作者循序渐进&#xff0c;讲的很透彻。下面一一剖析。 1.变量的作用域 变量的…

.NET 20周年专访 - 张善友:.NET 技术是如何赋能并改变世界的

点击蓝字关注我们今年是 .NET 发布20周年&#xff0c;值此20周年之际&#xff0c;微软 Reactor 特别策划了 .NET 20周年系列主题专访。我们邀请了数位中国 .NET 领域的技术专家以及社区名人&#xff0c;来聊聊他们与 .NET 的情缘、认识 .NET 的契机、选择 .NET 的理由&#xff…

【ArcGIS错误异常100问】之005:ArcGIS字段计算器python中文编码问题解决

问题描述&#xff1a; 现因工作的需要&#xff0c;对照2017最新版&#xff1a;《土地利用现状分类》&#xff08;GBT 21010-2017&#xff09;&#xff0c;需根据DLMC对DLBM进行批量修改&#xff0c;如旱地是0103&#xff0c;其他林地是0307等&#xff0c;共计19种用地类型。 问…

【ArcGIS微课1000例】0055:根据图层创建自定义图例符号案例教程

在利用ArcGIS作图时,有时候需要根据线状或面状图层自己的矢量形状去创建图例项目符号,本文讲解根据图层创建自定义图例符号。 本实验使用的数据为配套案例数据包中的0055.rar中的水库数据。 文章目录 1. 添加“新建图例图面形状”工具2. 根据图层形状创建符号3. 绘制形状符号…

真魔法!图形化管理 Kafka 超轻量的自动化工具

Kafka Magic[1] 是一个用于处理 Apache Kafka 集群的 GUI 工具。它可以查找和显示消息、在 Topic 之间转换和移动消息、查看和更新模式、管理 Topic 以及自动化复杂任务。Kafka Magic 通过方便的用户界面促进 Topic 管理、QA 和集成测试。Kafka Magic Community Edition 可免费…

【FME实战教程】001:FME2020中文安装图文教程(附安装包下载)

文章目录1. 安装license2. 安装FME Desktop3. 安装中文语言4. FME软件下载地址1. 安装license 打开软件安装包中的fme-flexnet-win-x64.msi&#xff0c;如下图所示&#xff1a; 点击Next。 点击Next。 单击install。 点击finish&#xff0c;完成。 &#xff08;1&#xff09;修…

算法导论 第三部分——基本数据结构——第14章:数据结构的扩张

本章通过扩张红黑树构造出两种数据结构&#xff1a;动态顺序统计和区间树。 1、动态顺序统计&#xff1a;查找倒数第i小的数据 复杂度为 lg(n) 为什么是扩张红黑树而不是搜索二叉树或者二叉树&#xff1f; 相对于搜索二叉树&#xff0c;红黑树的平衡性更好&#xff0c;高度在l…

/hgfs下无共享文件夹?/mnt下没有hgfs文件夹?vmhgfs-fuse:找不到命令?

前言&#xff1a;最近在使用linux的过程中&#xff0c;需要在宿主操作系统与客户操作系统间建立共享文件夹&#xff0c;遇到了些许问题&#xff0c;在网上参考了许多文章与各种尝试后&#xff0c;现得以解决&#xff0c;分享如下。1、系统环境&#xff1a;宿主操作系统&#xf…

GraphQL入门有

本文将从GraphQL是什么&#xff0c;为什么要使用GraphQL&#xff0c;使用GraphQL创建简单例子&#xff0c;以及GraphQL实战&#xff0c;四个方面对GraphQL进行阐述。说得不对的地方&#xff0c;希望大家指出斧正。 github项目地址&#xff1a;https://github.com/Charming2015/…

对话庄表伟:开源第一课

庄表伟目前就职于华为的开源管理中心。自2014年开源社成立之初&#xff0c;他便友情参与了开源社的筹办工作。2017年&#xff0c;开源社转型为完全由个人成员组成的组织&#xff0c;庄表伟就以个人身份加入了开源社。作为开源社理事&#xff0c;当被问到“为什么要参选”时&…

【FME实战教程】002:FME完美实现CAD数据转shp案例教程(以三调土地利用现状数据为例)

FME完美实现CAD数据转shp案例教程&#xff08;以三调土地利用数据为例&#xff09; 文章目录1. cad数据预览2. 转换过程3. shp数据预览1. cad数据预览 2. 转换过程 &#xff08;1&#xff09;打开FME Desktop2020中文软件&#xff0c;点击【新建】。 &#xff08;2&#xff09…