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 注解修饰,以保证接口中只有一个抽象方法,常用的函数式接口主要有:ConsumerPredicateFunctionComparator,用户可以根据入参个数和出参类型选择合适的函数式接口。

类型 类名 抽象方法 特点
消费型 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 表达式总结的更多相关文章

  1. 你知道C#中的Lambda表达式的演化过程吗?

    那得从很久很久以前说起了,记得那个时候... 懵懂的记得从前有个叫委托的东西是那么的高深难懂. 委托的使用 例一: 什么是委托? 个人理解:用来传递方法的类型.(用来传递数字的类型有int.float ...

  2. Linq表达式、Lambda表达式你更喜欢哪个?

    什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...

  3. 背后的故事之 - 快乐的Lambda表达式(一)

    快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...

  4. Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

    作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...

  5. java8中lambda表达式的应用,以及一些泛型相关

    语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...

  6. 背后的故事之 - 快乐的Lambda表达式(二)

    快乐的Lambda表达式 上一篇 背后的故事之 - 快乐的Lambda表达式(一)我们由浅入深的分析了一下Lambda表达式.知道了它和委托以及普通方法的区别,并且通过测试对比他们之间的性能,然后我们 ...

  7. CRL快速开发框架系列教程二(基于Lambda表达式查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  8. Lambda 表达式递归用法实例

    注意: 使用Lambda表达式会增加额外开销,但却有时候又蛮方便的. Windows下查找子孙窗口实例: HWND FindDescendantWindows(HWND hWndParent, LPC ...

  9. Spark中Lambda表达式的变量作用域

    通常,我们希望能够在lambda表达式的闭合方法或类中访问其他的变量,例如: package java8test; public class T1 { public static void main( ...

  10. 释放Android的函数式能量(I):Kotlin语言的Lambda表达式

    原文标题:Unleash functional power on Android (I): Kotlin lambdas 原文链接:http://antonioleiva.com/operator-o ...

随机推荐

  1. 【Linux API 揭秘】module_init与module_exit

    [Linux API 揭秘]module_init与module_exit Linux Version:6.6 Author:Donge Github:linux-api-insides 1.函数作用 ...

  2. [java] JSP post 提交乱码 解决方案

    //在post提交的页面顶部插入下列代码 <% request.setCharacterEncoding("UTF-8"); %>

  3. [转帖]使用systemd-analyze 工具来分析各个服务进程的启动性能

    https://www.cnblogs.com/xingmuxin/p/11413784.html systemd-analyze是一个分析启动性能的工具,用于分析启动时服务时间消耗.默认显示启动是内 ...

  4. [转帖]在 TiDB 中正确使用索引,性能提升 666 倍

    https://tidb.net/book/tidb-monthly/2022/2022-04/usercase/index-666 背景​ 最近在给一个物流系统做TiDB POC测试,这个系统是基于 ...

  5. [转帖]Linux—编写shell脚本操作数据库执行sql

    Linux-编写shell脚本操作数据库执行sql Hughman关注IP属地: 北京 0.0762020.03.20 09:02:13字数 295阅读 1,036 修改数据库数据   在升级应用时, ...

  6. [转帖]nginx的luajit安装luarocks并安装luafilesystem

    nginx的luajit安装luarocks并安装luafilesystem by admin on 2015-07-11 08:05:23 in , 69次 标题有点绕口.我尽量把关键词都贴进去.之 ...

  7. [转帖]BF16 与 FP16 在模型上哪个精度更高呢

    https://zhuanlan.zhihu.com/p/449345588 BF16 是对FP32单精度浮点数截断数据,即用8bit 表示指数,7bit 表示小数. FP16半精度浮点数,用5bit ...

  8. CentOS7 通过移植二进制文件的方式安装redis、nginx以及dotnet core的简单办法

    新的centos机器安装预制软件比较麻烦 最简单的方法是在保证服务器或者是虚拟机硬件架构相同,并且操作系统版本差别不是很大的情况下, 直接使用其他机器已经变异好的二进制文件最为简单. 比如本次 我这边 ...

  9. vCenter 6.7 使用Grafana监控失败的处理

    背景 国庆处理的vCenter监控. 老的vCenter6.0的平台很正常. 但是新的vCenter 6.7 就经常出现断连的情况. 花费了快一个多小时才搞定, 这里记录一下. 问题现象 vCente ...

  10. 吾爱破解 2023 春节解题领红包之 Web 题解

    (图作者 | 吾爱破解@Ps出来的小赵) 吾爱破解每年都有个解题领红包活动,今年也不例外,需要我们使出看家逆向本领来分析内容获得口令红包,根据难度等级不同会获得不同数量的吾爱币,活动持续到元宵节结束. ...