jdk8新特性----Lambda表达式

一、介绍

1、简介

Java的Lambda表达式是Java 8引入的一个特性,它支持函数式编程,允许将函数作为方法的参数或返回值,从而简化了匿名内部类的使用,并提供了对并行编程的更好支持。

可以将Lambda表达式理解为一个匿名函数; Lambda表达式允许将一个函数作为另外一个函数的参数; 我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码作为实参),也可以理解为函数式编程,将一个函数作为参数进行传递

2、优点

Lambda表达式的主要优势包括:

  • 简化匿名内部类的使用,使代码更加简洁明了。
  • 支持函数式编程,允许函数作为第一类对象进行传递和操作。
  • 促进并行编程,因为函数式编程中的纯函数天然具备无副作用的特性,使得在并行编程中更容易实现可靠的多线程和并行处理。
3、用途

Lambda表达式主要用于函数式接口,即只包含一个抽象方法的接口,可以使用@FunctionalInterface注解进行标识。常用场景有:

  • 创建Runnable实例:Runnable runnable = () -> {System.out.println("Hello, Lambda!");};
  • 实现Consumer接口:numbers.forEach(n -> System.out.println(n));
  • 实现Predicate接口:filteredNames = names.stream().filter(name -> name.length() > 5).collect(Collectors.toList());
  • 创建线程:new Thread(() -> System.out.println("haha")).start();

二、语法与使用

1、基本语法
([Lambda参数列表,即形参列表]) -> {Lambda体,即方法体}

  如果主体只有一行代码可以更简化,去掉{}:

(parameters) -> expression

(1)Lambda 表达式关注的是接口中方法的返回值和参数,方法名不重要
(2)使用 "->"将参数和实现逻辑分离;

       ( ) 中的部分是需要传入Lambda体中的参数,参数可以是任意合法的Java参数列表,可以为空或包含一个或多个参数;

        { } 中部分,接收来自 ( ) 中的参数,完成一定的功能。Lambda主体可以是一个表达式,也可以是一个代码块。如果主体是一个表达式,它将直接返回该表达式的结果。如果主体是一个代码块,它将按照常规的Java语法执行,并且您可能需要使用return语句来返回值。
(3)只有函数式接口的变量或者是函数式接口,才能够赋值为Lambda表达式。这个接口中,可以有默认方法,或者是静态方法。

2、使用条件

lambda表达式和方法引用使用前提:函数式接口
(1)@FunctionalInterface 语法格式严格要求当前接口有且只能有一个尚未完成的缺省属性为 public abstract 修饰方法。Lambda表达式的使用前提是存在一个接口,该接口中有且只有一个抽象方法。在主方法中可以通过创建接口的匿名内部类或使用Lambda表达式来调用该接口的方法。
(2)函数式接口一般用于方法的增强,直接作为方法的参数,实现函数式编程。只有函数式接口的变量或者是函数式接口,才能够赋值为Lambda表达式。这个接口中,可以有默认方法,或者是静态方法。

3、四种lambda表达式
(1)无参数无返回值
() -> System.out.println("Hello, Lambda!");

 如:

//接口设计
@FunctionalInterface
interface A {void 方法名真的没有用();
}
//方法设计
public static void testLambda(A a) {a.方法名真的没有用();
}
//代码实现
public static void main(String[] args) {//1.匿名内部类方法//接口无法实例化,这里实例化的是 A 接口的实现类对象(该方法在jdk1.8以后被lambda表达式完虐)testLambda(new A() {//缺省属性为abstract,需要重写@Overridepublic void 方法名真的没有用() {System.out.println("无参数返回值 匿名内部类对象方法实现");}});/*2. Lambda 表达式实现【分析】void 方法名真的没有用();接口方法【返回值类型】 void,无返回值接口方法【参数】 无参数*/testLambda(() -> {System.out.println("Lambda 表达式初体验");});//Lambda 表达式有且只有一行代码,可以省略大括号testLambda(() -> System.out.println("Lambda 表达式初体验"));
(2) 无参数有返回值
(x, y) -> System.out.println(x + y);(x, y) -> {int sum = x + y;System.out.println("Sum: " + sum);return sum;
}
//接口设计
@FunctionalInterface
interface Supplier<T> {/*** 无参数有返回值方法,泛型约束的是接口对应的返回值数据类型,要求按照泛型约束返回对应的数据内容** @return 返回一个数据,符合泛型约束*/T get();
}
/** 当前方法要求返回一个字符串数据内容*/
public static String testLambda(Supplier<String> s) {return s.get();
}
public static void main(String[] args) {/*【分析】T get(); ==> 泛型约束为 String ==> String get();接口方法【返回值类型】 String接口方法【参数】 无参数Lambda 格式:() -> {必须返回一个 String 类型}*/String s1 = testLambda(() -> {return "这里也是一个字符串";});System.out.println(s1);/*Lambda 优化,只要 -> 之后是一个 字符串数据内容就可以满足当前 Lambda 所需可以省略 return ,前提是当前 Lambda 有且只有一行代码*/String s2 = testLambda(() -> "这里也是一个字符串");System.out.println(s2);/*Lambda 内部使用使用方法局部变量*/String str = "name=王小明&age=23&country=中国";String s3 = testLambda(() -> {// str 是当前 main 方法局部变量,Lambda 内部可以直接使用String[] split = str.split("&");return split[0];});System.out.println(s3);//返回王小明
}
(3) 有参数无返回值
@FunctionalInterface
interface Consumer<T> {/*** 消费者接口,数据最终处理接口,数据处理终止方法接口,对应的方法要求方法有参数无返回值** @param t 泛型数据数据类型 T ,支持任意类型,在接口约束之后,要求符合数据类型一致化要求*/void accept(T t);
}
/*** 有参数无返回 Lambda 测试方法,方法参数是 String 类型和针对于 String 类型* 进行数据处理的 Consumer 接口,Consumer 接口可以传入实现类对象和 Lambda 表达式** @param str    目标处理的 String 字符串数据* @param handle 已经约束为处理 String 类型数据的 Consumer 接口处理器*/
public static void testLambda(String str, Consumer<String> handle) {handle.accept(str);
}
public static void main(String[] args) {/*1、匿名内部类 Low*/testLambda("孟州市炒面第一名", new Consumer<String>() {@Overridepublic void accept(String t) {System.out.println(t);}});/*2. Lambda 表达式【分析】void accept(T t); ==> 泛型约束为 String ==> void accept(String t);接口方法【返回值】 void接口方法【参数】 1 个参数,String 类型Lambda 格式Lambda 小括号中的临时变量名称,没有数据类型体现,需要【联想】目标方法数据类型只按照参数的个数定义临时小变量(s) -> {大括号中无需返回值类型}Lambda 表达式临时变量 s 对应的数据类型为 String 类型 【联想可得】*/testLambda("lambda表达式需要联想!!!", (s) -> {System.out.println(Arrays.toString(s.toCharArray()));});/*Lambda 优化1. 代码块有且只有一行,可以省略大括号2. 小括号中有且只有一个 参数,可以省略小括号【注意】Lambda 承担的角色是一个针对于 String 字符串的处理器*/testLambda("lambda表达式需要联想!!!", s -> System.out.println(Arrays.toString(s.toCharArray())));
}
(4)有参数有返回值 
4、方法引用(拓展):

当Lambda表达式满足某种条件的时候,使用方法引用,可以再次简化代码。

(1)构造引用

当Lambda表达式是通过new一个对象来完成的,那么可以使用构造引用。

import java.util.function.Supplier;
public class TestLambda {public static void main(String[] args) {
//        Supplier<Student> s = () -> new Student();Supplier<Student> s = Student::new;}//实际过程:将new Student()赋值给了Supplier这个函数式接口中的那个抽象方法
}
(2) 类名::实例方法

Lambda表达式的的Lambda体也是通过一个对象的方法完成,但是调用方法的对象是Lambda表达式的参数列表中的一个,剩下的参数正好是给这个方法的实参。

import java.util.TreeSet;
public class TestLambda {public static void main(String[] args) {TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
}
(3) 对象::实例方法
*/ //类名::实例方法TreeSet<String> set = new TreeSet<>(String::compareTo);set.add("Hello");set.add("isea_you");// set.forEach(t -> System.out.println(t));//Hello \n isea_youset.forEach(System.out::println);//(1)对象::实例方法,Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应}
}
(4)类名::静态方法 
package com.isea.java;
import java.util.stream.Stream;
public class TestLambda {public static void main(String[] args) {
//        Stream<Double> stream = Stream.generate(() -> Math.random());
//        类名::静态方法, Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应Stream<Double> stream = Stream.generate(Math::random);stream.forEach(System.out::println);}
}
5、变量捕获
5.1、匿名内部类的变量捕获

在Java中,匿名内部类可以捕获外部变量,即在匿名内部类中引用并访问外部作用域的变量。这种行为称为变量捕获(Variable Capturing)。在匿名内部类中,可以捕获以下类型的变量:

(1)实例变量(Instance Variables):如果匿名内部类位于一个实例方法中,它可以捕获并访问该实例的实例变量。

(2)静态变量(Static Variables):匿名内部类可以捕获并访问包含它的类的静态变量。

(3)方法参数(Method Parameters):匿名内部类可以捕获并访问包含它的方法的参数。

(4)本地变量(Local Variables):匿名内部类可以捕获并访问声明为final的本地变量。从Java 8开始,final关键字可以省略,但该变量实际上必须是最终的(即不可修改)。

当匿名内部类捕获变量时,它们实际上是在生成的字节码中创建了一个对该变量的副本。这意味着即使在外部作用域中的变量发生改变,匿名内部类中捕获的变量仍然保持其最初的值。demo:

public class OuterClass {private int instanceVariable = 10;private static int staticVariable = 20;public void method() {final int localVar = 30; // 或者直接使用 Java 8+ 的隐式 finalRunnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("Instance variable: " + instanceVariable);System.out.println("Static variable: " + staticVariable);System.out.println("Local variable: " + localVar);}};runnable.run();}
}
5.2、Lambda表达式的变量捕获 

在Lambda表达式中,同样可以捕获外部作用域的变量。Lambda表达式可以捕获以下类型的变量:

(1)实例变量(Instance Variables):Lambda表达式可以捕获并访问包含它的实例的实例变量。

(2)静态变量(Static Variables):Lambda表达式可以捕获并访问包含它的类的静态变量。

(3)方法参数(Method Parameters):Lambda表达式可以捕获并访问包含它的方法的参数。

(4)本地变量(Local Variables):Lambda表达式可以捕获并访问声明为final的本地变量。从Java 8开始,final关键字可以省略,但该变量实际上必须是最终的(即不可修改)。

与匿名内部类不同,Lambda表达式不会创建对变量的副本,而是直接访问变量本身。这意味着在Lambda表达式中捕获的变量在外部作用域中发生的改变也会在Lambda表达式中反映出来。demo:

public class LambdaVariableCapture {private int instanceVariable = 10;private static int staticVariable = 20;public void method() {int localVar = 30;// Lambda表达式捕获外部变量Runnable runnable = () -> {System.out.println("Instance variable: " + instanceVariable);System.out.println("Static variable: " + staticVariable);System.out.println("Local variable: " + localVar);};runnable.run();}
}

三、使用场景

1、在集合中的使用

Lambda表达式在Collection接口中的使用主要涉及对集合进行迭代、筛选和转换等操作。在Java 8及以上的版本中,Collection接口增加了一些默认方法,例如forEach()removeIf()stream()等,使得使用Lambda表达式更加方便。

1.1、Collection接口
(1)迭代
    List<String> stringList = Arrays.asList("apppe","organge","banana");// 原来的方式for (String s : stringList) {System.out.println(s);}// lambda 表达式stringList.forEach(s->{System.out.println(s);});
(2)筛选
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);numbers.removeIf(n -> n % 2 == 0);
(3)转换
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");List<Integer> nameLengths = names.stream().map(name -> name.length()).collect(Collectors.toList());
(4)获取集合流:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);Stream<Integer> stream = numbers.stream();
(5)使用filter()筛选集合元素:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");List<String> filteredNames = names.stream().filter(name -> name.startsWith("A")).collect(Collectors.toList());
1.2、 List接口
(1)遍历
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");fruits.forEach(fruit -> System.out.println(fruit));
(2)过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);List<Integer> evenNumbers = numbers.stream().filter(number -> number % 2 == 0).collect(Collectors.toList());
(3)映射
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");List<Integer> nameLengths = names.stream().map(name -> name.length()).collect(Collectors.toList());
(4)查找
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");Optional<String> foundFruit = fruits.stream().filter(fruit -> fruit.startsWith("B")).findFirst();
(5)排序
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 6, 3, 9, 4, 7, 10);List<Integer> sortedNumbers = numbers.stream().sorted().collect(Collectors.toList());
1.3、Map接口

(1)迭代Map的键值对:

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);map.forEach((key, value) -> System.out.println(key + ": " + value));

(2)遍历Map的键或值:

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);map.keySet().forEach(key -> System.out.println(key));
map.values().forEach(value -> System.out.println(value));

(3)使用Stream过滤Map的键值对:

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);Map<String, Integer> filteredMap = map.entrySet().stream().filter(entry -> entry.getValue() > 30).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
(4)对Map的值进行映射:
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);Map<String, String> mappedMap = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> "Age: " + entry.getValue()));
(5)对Map的键或值进行归约操作:
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);int sumOfValues = map.values().stream().reduce(0, (a, b) -> a + b);
String concatenatedKeys = map.keySet().stream().reduce("", (a, b) -> a + b);

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

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

相关文章

Kubernetes(k8s)的认证(Authentication)策略解析

Kubernetes&#xff08;k8s&#xff09;的认证&#xff08;Authentication&#xff09;策略是确保只有经过验证的实体&#xff08;用户、服务账户等&#xff09;能够访问API服务器的基础安全措施。Kubernetes支持多种认证方法&#xff0c;以下是主要的认证策略&#xff1a; X50…

【java9】java9新特性概述

经过4次的跳票&#xff0c;历经曲折的Java9最终在2017年9月21日发布。因为里面加入的模块化系统&#xff0c;在最初设想的时候并没有想过那么复杂&#xff0c;花费的时间超出预估时间。距离java8大约三年时间。 Java9提供了超过150项新功能特性&#xff0c;包括备受期待的模块…

MySQL基础入门【mysql初识 | 数据库操作 | 表操作 | sql数据类型】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;为什么会有…

【鸿蒙+全国产瑞芯微】智慧楼宇解决方案 | 如何实现多场景下智慧化、精细化楼宇管理?

随着数字化、智能化与工作生活的联结日渐紧密&#xff0c;聚焦人性化服务&#xff0c;以数字和科技匹配多重需求&#xff0c;加速商业楼宇智能化转型的脚步&#xff0c;逐步形成智慧楼宇产品矩阵。 方案亮点 01/数字标牌——形象展示 企业文化宣传、公告通知等 播放内容统一远…

uni-app跨端兼容

1.样式兼容 小程序端不支持*选择器&#xff0c;可以使用&#xff08;view,text&#xff09; 页面视口差异(tabar页、普通页) H5端默认开始scoped 例如骨架屏样式出现问题&#xff0c;需要将之前的样式拷贝到骨架屏中 提示&#xff1a;H5端是单页面应用&#xff0c;scoped隔离…

轴承制造企业“数智化”突破口

轴承是当代机械设备中一种重要零部件。它的主要功能是支撑机械旋转体&#xff0c;降低其运动过程中的摩擦系数&#xff0c;并保证其回转精度。轴承是工业核心基础零部件&#xff0c;对国民经济发展和国防建设起着重要的支撑作用。 轴承企业普遍采用以销定产的经营模式&#xf…

【排序算法】之希尔排序

一、算法介绍 希尔排序(Shell Sort)是插入排序的一种&#xff0c;它是针对直接插入排序算法的改进。希尔排序又称缩小增量排序&#xff0c;因 DL.Shell 于 1959 年提出而得名。它通过比较相距一定间隔的元素来进行&#xff0c;各趟比较所用的距离随着算法的进行而减小&#xf…

浮点数的乘法处理

1. 确定符号位&#xff1b; 这个比较好理解&#xff0c;有一个负数&#xff0c;就是负数&#xff0c;否则&#xff0c;就是正数&#xff1b; 2. 解码相加&#xff1b; 这个也比较好激烈&#xff0c;乘法就是指数相加&#xff1b; 3. 尾数相乘&#xff1b; 这里的乘法&…

消息队列——Kafka

1、什么是消息队列&#xff0c;什么是Kafka&#xff1f; 我们通常说的消息队列&#xff0c;简称MQ&#xff08;Message Queue&#xff09;&#xff0c;它其实就指消息中间件&#xff0c;比较流行的开源消息中间件有&#xff1a;Kafka、RabbitMQ、RocketMQ等。今天我们要介绍的…

qt移植到imx6ull运行(qt部署到imx6ull)

这个事情对于小白来说确实不是很友好&#xff0c;会经常出现错误&#xff0c;我弄了两天终于弄好了 我主要参考了https://blog.csdn.net/m0_61738650/article/details/131269561 https://blog.csdn.net/m0_61738650/article/details/131171914这两个教程 我现在来简述一下流程…

【项目】Boost搜索引擎

项目相关背景 现在市面上已经出现很多搜索引擎&#xff0c;比如&#xff1a;百度、Google、Bing等等&#xff0c;它们都是全网性搜索 而我做得项目就像cplusplus网站中搜索C的相关知识一样&#xff0c;同样做的是站内搜索&#xff0c;它的搜索更垂直。 搜索引擎的宏观原理 ser…

Linux本地部署Nightingale夜莺监控并实现远程访问提高运维效率

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

视频剪辑的技巧:掌握如何高效批量调整视频尺寸的方法

在视频剪辑的过程中&#xff0c;调整视频尺寸是一个常见的需求。无论是为了适应不同平台的播放要求&#xff0c;还是为了统一多个视频的尺寸以提升观看体验&#xff0c;掌握高效批量调整视频尺寸的技巧都显得尤为重要。本文将为您详细介绍云炫AI智剪如何高效地进行这一操作&…

通往糊涂之路 The road to serfdom

最近被推送了一本书&#xff0c;哈耶克的............ 试一试&#xff0c;看看能不能看懂&#xff0c;也许是通往糊涂之路。

记折磨我好几天的一个问题

先交代下背景吧&#xff1a; 我们的系统有很多板子用于跑测试&#xff0c;每一块板子对应一个docker 容器&#xff0c;在容器中跑shell脚本&#xff0c;会调用expect 脚本&#xff0c;在expect脚本中通过screen /dev/ttyUSBx 比特率 连接板子发送命令等&#xff0c;无异常 现…

特殊类的设计与单例模式

1、特殊类的设计 如何设计出一个创建出的对象只能在堆上的类&#xff1f;将类的默认构造函数设置为私有&#xff0c;再将类的拷贝构造函数设置为delete&#xff0c;设置静态函数GetObj&#xff0c;内部调用new HeapOnly&#xff0c;这样就只能在堆上开辟空间。 class HeapOnly…

Windows Qt中支持heic 图片显示

安装vcpkg&#xff1a; git clone https://github.com/microsoft/vcpkg 执行脚本&#xff1a; .\vcpkg\bootstrap-vcpkg.bat 在安装之前如果需要指定vs的编译器&#xff0c; 在如下文件中做更改&#xff0c; 我指定的是用vs2019编译的&#xff1a; D:\vcpkg\vcpkg\triplets 增…

DevOps与低代码:重塑软件开发与运维新时代

随着数字化转型的深入推进&#xff0c;软件开发和运维的界限变得越来越模糊。在这种背景下&#xff0c;DevOps理念应运而生&#xff0c;它强调开发和运维团队的紧密协作&#xff0c;以实现软件的高效交付和稳定运行。与此同时&#xff0c;低代码技术的发展也为软件开发带来了新…

谈 postman自动化接口测试

背景描述 有一个项目要使用postman进行接口测试&#xff0c;接口所需参数有&#xff1a; appid: 应用标识&#xff1b; sign&#xff1a;请求签名&#xff0c;需要使用HMACSHA1加密算法计算&#xff0c;签名串是&#xff1a;{appid}u r l {url}url{stamp}&#xff1b; stam…

AlphaFold3—转录因子预测(实操)

写在前面 我们上一次已经介绍了如何使用AlphaFold3&#xff1a;最新AlphaFold 3&#xff1a;预测所有生物分子结构、相互作用 AlphaFold3可以做什么&#xff1f; 1.AlphaFold服务器可以对以下生物分子类型进行建模&#xff0c;评价其相互结合&#xff1a; 蛋白质 DNA RNA 生…