Lambda 表达式总结
1 Lambda 表达式简介
Lambda 表达式是 JDK 8 的新特性,主要用于简化匿名内部类的定义,帮助用户方便、高效地书写优雅的代码。
Lambda 表达式实现的必须是一个接口,并且接口中只有一个抽象方法,可以有 Object 类的方法(equals 等)和 default 修饰的方法(JDK8 新特性,被 default 修饰的方法会有默认实现,子类可以不实现,所以不影响 Lambda 表达式的使用)。
被 @FunctionalInterface 注解修饰的接口,表示函数式接口,只有一个抽象方法,它和 lambda 表达式通常一起出现,JDK 也提供了大量的函数式接口供用户使用,如:Consumer、Supplier、Function、Predicate(四大核心函数式接口)等。
通常,在定义匿名内部类的时候,也创建了一个对象,如下:
interface Test {
public void fun();
}
//1.普通写法
Test test = new Test() {
@Override
public void fun() {
System.out.println("xxx");;
}
};
test.fun(); //打印:xxx
//2.Lambda 表达式写法
Test test = () -> System.out.printf("xxx");
test.fun(); //打印:xxx
可以看到,Lambda 表达式使代码看起来非常简洁优雅。
2 Lambda 表达式语法
2.1 基础用法
Lambda 表达式主要用于简化匿名内部类的定义,并且被实现的接口只有一个抽象方法,因此本节将对此抽象方法的入参个数展开讨论。
(1)无参数
interface Test {
public void fun();
}
//1.一条语句
Test test = () -> System.out.println("xxx");
test.fun(); //打印:xxx
//2.多条语句
Test test = () -> {
System.out.println("xxx");
System.out.println("yyy");
};
test.fun(); //打印:xxx \n yyy
当接口中的抽象方法有返回值时, Lambda 表达式 使用方法如下:
interface Test {
public int fun();
}
//1.一条语句,并且是 return 语句
Test test = () -> 1 + 1;
int a = test.fun();
System.out.println(a); //打印:2
//2.多条语句
Test test = () -> {
int x = 1 + 1;
return x + 1;
};
int a = test.fun();
System.out.println(a); //打印:3
(2)单参数
interface Test {
public void fun(int x);
}
//Test test = x -> System.out.println(x);
Test test = (x) -> System.out.println(x);
test.fun(1); //打印:1
入参为单参数时,可以省略入参小括号(见注释行)。
(3)多参数
interface Test {
public void fun(int x, int y);
}
Test test = (x, y) -> System.out.println(x + y);
test.fun(1, 2); //打印:3
2.2 方法映射
方法映射是指:将待重写的方法映射到一个已知方法,前提是:待映射的方法和目标方法的入参个数、类型和返回值类型一致。
interface Test {
public int fun(int x, int y);
}
public class Main {
public static void main(String[] args) {
//1.映射到静态方法
Test test1 = Main::add; //映射:fun -> add
int a = test1.fun(1, 2);
System.out.println(a); //打印:3
//2.映射到实例方法
Main main = new Main();
Test test2 = main::subtract; //映射:fun -> subtract
int b = test2.fun(1, 2);
System.out.println(b); //打印:-1
}
private static int add(int x, int y) {
return x + y;
}
private int subtract(int x, int y) {
return x - y;
}
}
2.3 构造方法
需要创建的 User 类如下:
class User {
String name;
int id;
User(){
name = "张三";
id = 1001;
}
User(String name, int id) {
this.name = name;
this.id = id;
}
public String toString() {
return "User{name: " + name + ", id: " + id +"}";
}
}
(1)显式调用构造方法
显式调用构造方法是指: 显式调用 new + 构造方法。
interface Test {
public User newUser();
}
//1.无参构造方法
Test test = () -> new User();
User user = test.newUser();
System.out.println(user); //打印:User{name: 张三, id: 1001}
//2.有参构造方法
Test test = () -> new User("李四", 1002);
User user = test.newUser();
System.out.println(user); //打印:User{name: 李四, id: 1002}
(2)隐式调用构造方法
隐式调用构造方法是指: 通过匹配待实现接口中的函数入参,自动选择相应的构造方法。
interface Test {
public User newUser();
}
//1.无参构造方法
Test test = User::new; //自动匹配无参构造方法
User user = test.newUser();
System.out.println(user); //打印:User{name: 张三, id: 1001}
---------------------------------------------------------------
interface Test {
public User newUser(String name, int id);
}
//2.有参构造方法
Test test = User::new; //自动匹配有参构造方法
User user = test.newUser("李四", 1002);
//User user = test.newUser(); //编译报错
System.out.println(user); //打印:User{name: 李四, id: 1002}
3 常用函数式接口
函数式接口在一般在 java.util.function 包中,接口被 @FunctionalInterface 注解修饰,以保证接口中只有一个抽象方法,常用的函数式接口主要有:Consumer、Predicate、Function、Comparator,用户可以根据入参个数和出参类型选择合适的函数式接口。
类型 | 类名 | 抽象方法 | 特点 |
---|---|---|---|
消费型 | Consumer | void accept(T t) | 执行对 T、U 的操作事件 |
BiConsumer | void accept(T t, U u) | ||
供给型 | Supplier | T get() | 提供 T 类型对象 |
函数型 | Function | R apply(T t) | 对 T、U 应用操作,并返回 R 类型对象 |
BiFunction | R apply(T t, U u) | ||
断言型 | Predicate | boolean test(T t) | 判断 T、U 对象是否满足约束条件 |
BiPredicate | boolean test(T t, U u) | ||
其他 | Comparator | int compare(T o1, T o2) | 定义 T 类型对象的大小属性 |
说明:Comparator 不在 java.util.function 包中,而在 java.util 包中,Consumer、Supplier、Function、Predicate 并称为 Java 8 内置四大核心函数式接口。
函数式接口在 Optional 中广泛应用(四大核心函数式接口都用到了),详见→Optional 详解。
4 Lambda 表达式在集合中的应用
4.1 集合框架
集合框架简图如下,详细见→Java容器及其常用方法汇总
4.2 遍历 List 元素(forEach)
(1)源码
Iterable.java
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
(2)应用
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 4,3,1,5,2);
//1.方法一
list.forEach(x -> System.out.print(x)); //打印:43152
//Consumer<Integer> consumer = x -> System.out.print(x);
//list.forEach(consumer);
//2.方法二
list.forEach(System.out::print); //打印:43152
//Consumer<Integer> consumer = System.out::print;
//list.forEach(consumer);
4.3 遍历 Map 元素(forEach)
(1)源码
Map.java
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();
}
...
action.accept(k, v);
}
}
(2)应用
Map<Integer, String> map = new HashMap<>();
map.put(3, "qaz");
map.put(1, "wsx");
map.put(5, "edc");
map.forEach((i, s) -> System.out.print("(" + i + ", " + s +")")); //打印:(1, wsx)(3, qaz)(5, edc)
//BiConsumer<Integer, String> biConsumer = (i, s) -> System.out.print("(" + i + ", " + s +")");
//map.forEach(biConsumer);
4.4 删除元素(removeIf)
(1)源码
Collection.java
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
(2)应用
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 4,3,1,5,2);
list.removeIf(x -> x == 3);
//Predicate<Integer> predicate = x -> x == 3;
//list.removeIf(predicate);
list.forEach(System.out::print); //打印:4152
4.5 排序元素(sort)
(1)源码
List.java
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
(2)应用
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 4,3,1,5,2);
list.sort((x, y) -> x - y);
//Comparator<Integer> comparator = (x, y) -> x - y;
//list.sort(comparator);
list.forEach(System.out::print); //打印:12345
4.6 多关键字排序
(1)源码
Comparator.java
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
说明:thenComparing() 方法返回一个 Comparator 对象,其后可以再接多个 thenComparing() 方法,这里采用了职责链设计模式,详见→职责链模式。
(2)应用
//先按 name 排序,再按 age 排序
userList.sort(Comparator.comparing(User::getName).thenComparing(User::getAge));
5 Lambda 表达式闭包问题
匿名内部类通常定义在方法里,而方法里定义的变量都是局部变量,这时就可能存在此场景:匿名内部类访问方法中定义的局部变量。Java 语法要求:匿名内部类只能访问方法中定义的常量(被 final 修饰),否则会报错,如下:
Lambda 表达式放松了这一条件,但是在访问方法中定义的局部变量后,JVM 会自动将此变量改为常量,因此,之后修改此变量,会编译出错,如下:
声明:本文转自Lambda 表达式总结
Lambda 表达式总结的更多相关文章
- 你知道C#中的Lambda表达式的演化过程吗?
那得从很久很久以前说起了,记得那个时候... 懵懂的记得从前有个叫委托的东西是那么的高深难懂. 委托的使用 例一: 什么是委托? 个人理解:用来传递方法的类型.(用来传递数字的类型有int.float ...
- Linq表达式、Lambda表达式你更喜欢哪个?
什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...
- 背后的故事之 - 快乐的Lambda表达式(一)
快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...
- Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)
作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...
- java8中lambda表达式的应用,以及一些泛型相关
语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...
- 背后的故事之 - 快乐的Lambda表达式(二)
快乐的Lambda表达式 上一篇 背后的故事之 - 快乐的Lambda表达式(一)我们由浅入深的分析了一下Lambda表达式.知道了它和委托以及普通方法的区别,并且通过测试对比他们之间的性能,然后我们 ...
- CRL快速开发框架系列教程二(基于Lambda表达式查询)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- Lambda 表达式递归用法实例
注意: 使用Lambda表达式会增加额外开销,但却有时候又蛮方便的. Windows下查找子孙窗口实例: HWND FindDescendantWindows(HWND hWndParent, LPC ...
- Spark中Lambda表达式的变量作用域
通常,我们希望能够在lambda表达式的闭合方法或类中访问其他的变量,例如: package java8test; public class T1 { public static void main( ...
- 释放Android的函数式能量(I):Kotlin语言的Lambda表达式
原文标题:Unleash functional power on Android (I): Kotlin lambdas 原文链接:http://antonioleiva.com/operator-o ...
随机推荐
- 【Linux API 揭秘】module_init与module_exit
[Linux API 揭秘]module_init与module_exit Linux Version:6.6 Author:Donge Github:linux-api-insides 1.函数作用 ...
- [java] JSP post 提交乱码 解决方案
//在post提交的页面顶部插入下列代码 <% request.setCharacterEncoding("UTF-8"); %>
- [转帖]使用systemd-analyze 工具来分析各个服务进程的启动性能
https://www.cnblogs.com/xingmuxin/p/11413784.html systemd-analyze是一个分析启动性能的工具,用于分析启动时服务时间消耗.默认显示启动是内 ...
- [转帖]在 TiDB 中正确使用索引,性能提升 666 倍
https://tidb.net/book/tidb-monthly/2022/2022-04/usercase/index-666 背景 最近在给一个物流系统做TiDB POC测试,这个系统是基于 ...
- [转帖]Linux—编写shell脚本操作数据库执行sql
Linux-编写shell脚本操作数据库执行sql Hughman关注IP属地: 北京 0.0762020.03.20 09:02:13字数 295阅读 1,036 修改数据库数据 在升级应用时, ...
- [转帖]nginx的luajit安装luarocks并安装luafilesystem
nginx的luajit安装luarocks并安装luafilesystem by admin on 2015-07-11 08:05:23 in , 69次 标题有点绕口.我尽量把关键词都贴进去.之 ...
- [转帖]BF16 与 FP16 在模型上哪个精度更高呢
https://zhuanlan.zhihu.com/p/449345588 BF16 是对FP32单精度浮点数截断数据,即用8bit 表示指数,7bit 表示小数. FP16半精度浮点数,用5bit ...
- CentOS7 通过移植二进制文件的方式安装redis、nginx以及dotnet core的简单办法
新的centos机器安装预制软件比较麻烦 最简单的方法是在保证服务器或者是虚拟机硬件架构相同,并且操作系统版本差别不是很大的情况下, 直接使用其他机器已经变异好的二进制文件最为简单. 比如本次 我这边 ...
- vCenter 6.7 使用Grafana监控失败的处理
背景 国庆处理的vCenter监控. 老的vCenter6.0的平台很正常. 但是新的vCenter 6.7 就经常出现断连的情况. 花费了快一个多小时才搞定, 这里记录一下. 问题现象 vCente ...
- 吾爱破解 2023 春节解题领红包之 Web 题解
(图作者 | 吾爱破解@Ps出来的小赵) 吾爱破解每年都有个解题领红包活动,今年也不例外,需要我们使出看家逆向本领来分析内容获得口令红包,根据难度等级不同会获得不同数量的吾爱币,活动持续到元宵节结束. ...