函数式编程(JAVA)
# 函数式编程思想
它属于“结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。函数式编程比面向对象的优势就是粒度更小,生命周期更短。减少bug的有效途径就是减少变量的生命周期,缩小模块的粒度。 -------------------------------------------------------------------------------------摘自函数式编程思想 (opens new window)
# Lambda表达式
# 概述
Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口,它是函数式编程思想的一个重要体现。
下述所有测试代码源码地址:https://gitee.com/ArnoldSu/functionalProgramming.git (opens new window)
# 基本格式
(参数列表)->{代码}
初始化之后所有演示代码需要用到的数据集合
public static User createUser() {
return new User()
.setId(RandomUtil.randomLong())
.setAge(RandomUtil.randomInt(0, 100))
.setName(RandomUtil.randomString("澄邈德泽海超海阳海荣海逸海昌瀚钰瀚文涵亮涵煦明宇涵衍浩皛浩波浩博浩初浩宕浩歌浩广浩邈浩气浩思浩言鸿宝鸿波鸿博鸿才鸿畅鸿畴鸿达鸿德鸿飞鸿风鸿福鸿光鸿晖鸿朗鸿文鸿轩鸿煊鸿骞鸿远鸿云鸿哲鸿祯鸿志鸿卓嘉澍光济澎湃彭泽鹏池鹏海浦和浦泽瑞渊越泽博耘德运辰宇辰皓辰钊辰铭辰锟辰阳辰韦辰良辰沛晨轩晨涛晨濡晨潍鸿振吉星铭晨起运运凡运凯运鹏运浩运诚运良运鸿运锋运盛运升运杰运珧运骏运凯运乾维运运晟运莱运华耘豪星爵星腾星睿星泽星鹏星然震轩震博康震震博振强振博振华振锐振凯振海振国振平昂然昂雄昂杰昂熙昌勋昌盛昌淼昌茂昌黎昌燎昌翰晨朗德明德昌德曜范明飞昂高旻晗日昊然昊天昊苍昊英昊宇昊嘉昊明昊伟昊硕昊磊昊东鸿晖鸿朗华晖金鹏晋鹏敬曦景明景天景浩俊晖君昊昆琦昆鹏昆纬昆宇昆锐昆卉昆峰昆颉昆谊昆皓昆鹏昆明昆杰昆雄昆纶鹏涛鹏煊曦晨曦之新曦旭彬旭尧旭鹏旭东旭炎炫明宣朗学智轩昂彦昌曜坤曜栋曜文曜曦曜灿曜瑞智伟智杰智刚智阳昌勋昌盛昌茂昌黎昌燎昌翰晨朗昂然昂雄昂杰昂熙范明飞昂高朗高旻德明德昌德曜智伟智杰智刚智阳瀚彭旭炎宣朗学智昊然昊天昊苍昊英昊宇昊嘉昊明昊伟鸿朗华晖金鹏晋鹏敬曦景明景天景浩景行景中景逸景彰昆鹏昆明昆杰昆雄昆纶鹏涛鹏煊景平俊晖君昊昆琦昆鹏昆纬昆宇昆锐昆卉昆峰昆颉昆谊轩昂彦昌曜坤曜文曜曦曜灿曜瑞曦晨曦之新曦鑫鹏旭彬旭尧旭鹏旭东浩轩浩瀚浩慨浩阔鸿熙鸿羲鸿禧鸿信泽洋泽雨哲瀚胤运佑运允晨运恒运发云天耘志耘涛振荣振翱中震子辰晗昱瀚玥瀚昂瀚彭景行景中景逸景彰绍晖文景曦哲永昌子昂智宇智晖晗日晗昱瀚昂昊硕昊磊昊东鸿晖绍晖文昂文景曦哲永昌子昂智宇智晖浩然鸿运辰龙运珹振宇高朗景平鑫鹏昌淼炫明昆皓曜栋文昂治汇", 2))
.setNickname(RandomUtil.randomString("风雨踏梦行 樱花味女孩 柠檬泪 日之夕矣 半世浮生 逆水寒 雨后初晴 陌路 不缺友人 听风说旧人 舟不渡我 哑剧 沫丶白色控 傻子快来~ 厌己 森屿海巷 断桥再见 坚毅之梦 щǒ冇寎 遗忘 梦想的翅膀 捂着心脏说胃疼 南巷孤猫i 手捧阳光 樱花涵 指尖微凉° 你给的承诺丶早已泛黄 尐懒蟲ゞ 坚强的另一个名字叫苦撑 孤街浪人 瞌睡虫 薄荷凉 孤魄 热巴 温柔似你眉眼 离心咒ゝ 那一抹浅笑隱藏忧伤 盛夏北梦初心未改 幸福式梦想※ 唯念 依然血红。 沦陷的痛", 5))
.setSex(RandomUtil.randomInt(0, 2))
.setDep(createDepartment());
}
public static List<Department> createDepartment() {
List<Department> departments = new ArrayList<Department>();
int i = RandomUtil.randomInt(1, 4);
for (int j = 0; j < i; j++) {
departments.add(new Department().setId(RandomUtil.randomLong())
.setName(RandomUtil.randomString("人事技术前台前端后端", 2))
.setCode(RandomUtil.randomInt(1, 10000))
.setParentId(RandomUtil.randomLong(1, 10000))
);
}
return departments;
}
public static List<User> initUsers = new ArrayList<User>();
@BeforeAll
public static void init() {
for (int i = 0; i < 10; i++) {
initUsers.add(createUser());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
测试lambda表达式输出打印
/**
* description 测试lambda表达式输出打印用户信息
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void Test() {
initUsers.forEach(user -> System.out.println(user.toString()));
}
2
3
4
5
6
7
8
9
10
11
12
# Lambda省略规则
- 不需要声明参数类型,编译器可以统一识别参数值。
- 一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 如果主体包含了一个语句,就不需要使用大括号。
- 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
# Lambda 方法引用
引用数据类型最大的特点是可以进行内存的指向处理,但是在传统的开发之中一直所使用的只是对象引用操作,而jdk1.8以后也提供有方法的引用,即:不同的方法名称可以描述同一个方法(即可以为一个方法定义多个名字,但是要求必须是函数式接口)。如果要进行方法的引用在java里面提供了如下的四种形式
- 引用静态方法: 类名称 :: static方法名称;
- 引用某个实例对象的方法: 实例化对象 :: 普通方法;
- 引用特定类型的方法: 特定类 :: 普通方法;
- 引用构造方法: 类名称 :: new ;
# Stream流
Java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合或数组进行链状流式的操作。可以更方便的让我们对集合或数组操作。
下述所有测试代码源码地址:https://gitee.com/ArnoldSu/functionalProgramming.git (opens new window)
初始化之后所有演示代码需要用到的数据集合。
public static User createUser() {
return new User()
.setId(RandomUtil.randomLong())
.setAge(RandomUtil.randomInt(0, 100))
.setName(RandomUtil.randomString("澄邈德泽海超海阳海荣海逸海昌瀚钰瀚文涵亮涵煦明宇涵衍浩皛浩波浩博浩初浩宕浩歌浩广浩邈浩气浩思浩言鸿宝鸿波鸿博鸿才鸿畅鸿畴鸿达鸿德鸿飞鸿风鸿福鸿光鸿晖鸿朗鸿文鸿轩鸿煊鸿骞鸿远鸿云鸿哲鸿祯鸿志鸿卓嘉澍光济澎湃彭泽鹏池鹏海浦和浦泽瑞渊越泽博耘德运辰宇辰皓辰钊辰铭辰锟辰阳辰韦辰良辰沛晨轩晨涛晨濡晨潍鸿振吉星铭晨起运运凡运凯运鹏运浩运诚运良运鸿运锋运盛运升运杰运珧运骏运凯运乾维运运晟运莱运华耘豪星爵星腾星睿星泽星鹏星然震轩震博康震震博振强振博振华振锐振凯振海振国振平昂然昂雄昂杰昂熙昌勋昌盛昌淼昌茂昌黎昌燎昌翰晨朗德明德昌德曜范明飞昂高旻晗日昊然昊天昊苍昊英昊宇昊嘉昊明昊伟昊硕昊磊昊东鸿晖鸿朗华晖金鹏晋鹏敬曦景明景天景浩俊晖君昊昆琦昆鹏昆纬昆宇昆锐昆卉昆峰昆颉昆谊昆皓昆鹏昆明昆杰昆雄昆纶鹏涛鹏煊曦晨曦之新曦旭彬旭尧旭鹏旭东旭炎炫明宣朗学智轩昂彦昌曜坤曜栋曜文曜曦曜灿曜瑞智伟智杰智刚智阳昌勋昌盛昌茂昌黎昌燎昌翰晨朗昂然昂雄昂杰昂熙范明飞昂高朗高旻德明德昌德曜智伟智杰智刚智阳瀚彭旭炎宣朗学智昊然昊天昊苍昊英昊宇昊嘉昊明昊伟鸿朗华晖金鹏晋鹏敬曦景明景天景浩景行景中景逸景彰昆鹏昆明昆杰昆雄昆纶鹏涛鹏煊景平俊晖君昊昆琦昆鹏昆纬昆宇昆锐昆卉昆峰昆颉昆谊轩昂彦昌曜坤曜文曜曦曜灿曜瑞曦晨曦之新曦鑫鹏旭彬旭尧旭鹏旭东浩轩浩瀚浩慨浩阔鸿熙鸿羲鸿禧鸿信泽洋泽雨哲瀚胤运佑运允晨运恒运发云天耘志耘涛振荣振翱中震子辰晗昱瀚玥瀚昂瀚彭景行景中景逸景彰绍晖文景曦哲永昌子昂智宇智晖晗日晗昱瀚昂昊硕昊磊昊东鸿晖绍晖文昂文景曦哲永昌子昂智宇智晖浩然鸿运辰龙运珹振宇高朗景平鑫鹏昌淼炫明昆皓曜栋文昂治汇", 2))
.setNickname(RandomUtil.randomString("风雨踏梦行 樱花味女孩 柠檬泪 日之夕矣 半世浮生 逆水寒 雨后初晴 陌路 不缺友人 听风说旧人 舟不渡我 哑剧 沫丶白色控 傻子快来~ 厌己 森屿海巷 断桥再见 坚毅之梦 щǒ冇寎 遗忘 梦想的翅膀 捂着心脏说胃疼 南巷孤猫i 手捧阳光 樱花涵 指尖微凉° 你给的承诺丶早已泛黄 尐懒蟲ゞ 坚强的另一个名字叫苦撑 孤街浪人 瞌睡虫 薄荷凉 孤魄 热巴 温柔似你眉眼 离心咒ゝ 那一抹浅笑隱藏忧伤 盛夏北梦初心未改 幸福式梦想※ 唯念 依然血红。 沦陷的痛", 5))
.setSex(RandomUtil.randomInt(0, 2))
.setDep(createDepartment());
}
public static List<Department> createDepartment() {
List<Department> departments = new ArrayList<Department>();
int i = RandomUtil.randomInt(1, 4);
for (int j = 0; j < i; j++) {
departments.add(new Department().setId(RandomUtil.randomLong())
.setName(RandomUtil.randomString("人事技术前台前端后端", 2))
.setCode(RandomUtil.randomInt(1, 10000))
.setParentId(RandomUtil.randomLong(1, 10000))
);
}
return departments;
}
public static List<User> initUsers = new ArrayList<User>();
@BeforeAll
public static void init() {
for (int i = 0; i < 10; i++) {
initUsers.add(createUser());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 常用操作
# filter
可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。
eg:过滤用户年龄大于80岁的人并且打印。
/**
* description Stream filter 对流中的参数进行条件过滤(过滤用户年龄大于80岁的人并且打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void filterTest() {
initUsers.stream().filter(user -> user.getAge() > 80).forEach(user -> System.out.println(user.toString()));
}
2
3
4
5
6
7
8
9
10
11
12
# map
可以把对流中的元素进行计算或转换。
eg: 通过map获取用户的姓名并且遍历打印。
/**
* description Stream map 对流中的参数进行计算和转换(通过map获取用户的姓名并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void mapTest() {
initUsers.stream().map(User::getName).forEach(userName -> System.out.println(userName));
}
2
3
4
5
6
7
8
9
10
11
12
# distinct
可以去除流中的重复元素。
eg:通过map获取用户的姓名去重并且遍历打印。
/**
* description Stream distinct 对流中的参数进行去重(通过map获取用户的姓名去重并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void distinctTest() {
List<User> users = new ArrayList<>();
users.add(new User().setName("重复名称").setAge(1));
users.add(new User().setName("重复名称").setAge(2));
users.stream().map(User::getName).distinct().forEach(userName -> System.out.println(userName));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。
# sorted
可以对流中的元素进行排序。
eg: 通过用户的年龄升序排序并且遍历打印。
/**
* description Stream sorted 对流中的参数进行排序(通过用户的年龄升序排序并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void sortedTest() {
initUsers.stream().sorted(Comparator.comparingInt(User::getAge)).forEach(user -> System.out.println(user));
}/**
* description Stream limit 设置流的最大长度,超出的部分将被抛弃(获取三个用户信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void limitTest() {
initUsers.stream().limit(3).forEach(user -> System.out.println(user));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。
# limit
可以设置流的最大长度,超出的部分将被抛弃。
eg:获取三个用户信息并且遍历打印。
/**
* description Stream limit 设置流的最大长度,超出的部分将被抛弃(获取三个用户信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void limitTest() {
initUsers.stream().limit(3).forEach(user -> System.out.println(user));
}
2
3
4
5
6
7
8
9
10
11
12
# skip
跳过流中的前n个元素,返回剩下的元素。
eg:跳过前五个用户输出剩下的用户信息并且遍历打印。
/**
* description Stream skip 跳过流中的前n个元素,返回剩下的元素。(跳过前五个用户输出剩下的用户信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void skipTest() {
initUsers.stream().skip(5).forEach(user -> System.out.println(user));
}
2
3
4
5
6
7
8
9
10
11
12
# flatMap
map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
eg:输出用户下的部门信息并且遍历打印。
/**
* description Stream flatMap map只能把一个对象转换成另一个对象来作为流中的元素。
* 而flatMap可以把一个对象转换成多个对象作为流中的元素。(输出用户下的部门信息并且遍历打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void flatMapTest() {
initUsers.stream().flatMap(user -> user.getDep().stream()).forEach(department -> System.out.println(department));
}
2
3
4
5
6
7
8
9
10
11
12
13
# count
可以用来获取当前流中元素的个数。
eg:获取stream中的元素个数并且打印。
/**
* description Stream count 可以用来获取当前流中元素的个数。(获取stream中的元素个数并且打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void countTest() {
System.out.println(initUsers.stream().count());
}
2
3
4
5
6
7
8
9
10
11
12
# max&min
可以用来或者流中的最值。
eg:获取用户年龄最大的人/最小年龄人。
/**
* description Stream max/min 可以用来获取当前流中元素最值大和最小。(获取用户年龄最大的人/最小年龄人)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void maxOrMinTest() {
Optional maxOptional = initUsers.stream().max(Comparator.comparing(User::getAge));
System.out.println(maxOptional.get());
Optional minOptional = initUsers.stream().min(Comparator.comparing(User::getAge));
System.out.println(minOptional.get());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# collect基础用法
把当前流转换成一个集合。
eg:获取用户的昵称转集合并且遍历输出打印。
/**
* description Stream collect 把当前流转换成一个集合。(获取用户的昵称转集合并且遍历输出打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void collectTest() {
initUsers.stream().map(User::getNickName).collect(Collectors.toList()).stream().forEach(nickName -> System.out.println(nickName));
}
2
3
4
5
6
7
8
9
10
11
12
# List 转 Map的常用四种用法
//List 转 Map的常用四种用法:
//第⼀种: 取list中某2个字段作为Map的K,V
Map<String, Integer> map1 = initUsers.stream().collect(Collectors.toMap(User::getNickName, User::getAge));
map1.forEach((key, value) -> System.out.println(key + "=====" + value));
//第⼆种:将id和实体Bean做为K,V
Map<String, User> map2 = initUsers.stream().collect(Collectors.toMap(User::getNickName, user -> user));
map2.forEach((key, value) -> System.out.println(key + "=====" + value));
//第三种: key存在重复记录时处理
Map<String, User> map3 = initUsers.stream().collect(Collectors.toMap(User::getNickName, Function.identity(), (key1, key2) -> key2));
map3.forEach((key, value) -> System.out.println(key + "=====" + value));
//第四种: 使⽤某个具体的Map类来保存,如保存时使⽤LinkedHashMap
Map<String, User> map4 = initUsers.stream().collect(Collectors.toMap(User::getNickName, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));
map4.forEach((key, value) -> System.out.println(key + "=====" + value));
2
3
4
5
6
7
8
9
10
11
12
13
# anyMatch
用来判断是否有任意符合匹配条件的元素,结果为boolean类型。
eg:判断是否有用户年龄大于90岁人员。
/**
* description Stream anyMatch 用来判断是否有任意符合匹配条件的元素,结果为boolean类型。(判断是否有用户年龄大于90岁人员)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void anyMatchTest() {
System.out.println(initUsers.stream().anyMatch(user -> user.getAge() > 90));
}
2
3
4
5
6
7
8
9
10
11
12
# allMatch
用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。
eg:判断是否所有用户年龄大于60岁人员。
/**
* description Stream allMatch 可以用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。(判断是否所有用户年龄大于60岁人员)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void allMatchTest() {
System.out.println(initUsers.stream().allMatch(user -> user.getAge() > 60));
}
2
3
4
5
6
7
8
9
10
11
12
# noneMatch
判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false
eg:判断是否所有用户年龄都不大于98岁人员。
/**
* description Stream noneMatch 判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false。(判断是否所有用户年龄都不大于98岁人员)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void noneMatchTest() {
System.out.println(initUsers.stream().noneMatch(user -> user.getAge() > 98));
}
2
3
4
5
6
7
8
9
10
11
12
# findAny
获取流中的任意一个元素。
eg:随机获取一个人员信息并且打印。
/**
* description Stream findAny 获取流中的任意一个元素。(随机获取一个人员信息并且打印)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void findAnyTest() {
System.out.println(initUsers.stream().findAny());
}
2
3
4
5
6
7
8
9
10
11
12
# findFirst
获取流中的第一个元素。
eg:获取人员中岁数最小的一个人员信息。
/**
* description Stream findFirst 获取流中的第一个元素。(获取人员中岁数最小的一个人员信息)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void findFirstTest() {
System.out.println(initUsers.stream().sorted(Comparator.comparing(User::getAge)).findFirst().get());
}
2
3
4
5
6
7
8
9
10
11
12
# reduce
对流中的数据按照你指定的计算方式计算出一个结果。
reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果再和后面的元素计算。
eg:计算所有用户的年龄之和。
/**
* description Stream reduce 对流中的数据按照你指定的计算方式计算出一个结果。(计算所有用户的年龄之和)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void reduceTest() {
System.out.println(initUsers.stream().mapToInt(User::getAge).reduce(0, Integer::sum));
}
2
3
4
5
6
7
8
9
10
11
12
# 注意事项
- 流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
- 不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)
# Optional类
我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。 尤其是对象中的属性还是一个对象的情况下。这种判断会更多。而过多的判断语句会让我们的代码显得臃肿不堪。所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。
# Optional类的常用操作
该类的其实很简单,整体字段属性方法如下图:
下述所有测试代码源码地址:https://gitee.com/ArnoldSu/functionalProgramming.git (opens new window)
# of
返回一个Optional对象并且参数必须不能为空,如果为空则报错NullPointerException。
/**
* description Optional of 返回一个Optional对象。(返回一个Optional并且参数必须不能为空,如果为空则报错NullPointerException)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void ofTest() {
Optional<User> userOptional = Optional.of(initUser);
System.out.println(userOptional);
Optional<User> nullUserOptional = Optional.of(null);
System.out.println(nullUserOptional);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ofNullable
返回一个Optional对象。(返回一个Optional并且参数可以为null)。
/**
* description Optional ofNullable 返回一个Optional对象。(返回一个Optional并且参数可以为null)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void ofNullableTest() {
Optional<User> userOptional = Optional.ofNullable(initUser);
System.out.println(userOptional);
Optional<User> nullUserOptional = Optional.ofNullable(null);
System.out.println(nullUserOptional);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# empty
返回一个Optional对象。(返回一个Optional并且参数可以为null)。
/**
* description Optional empty 返回一个空Optional对象。
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void emptyTest() {
System.out.println(Optional.empty());
}
2
3
4
5
6
7
8
9
10
11
12
# get
返回Optional对象中的值。(为null 则会报错NoSuchElementException)。
/**
* description Optional get 返回Optional对象中的值。(为null 则会报错NoSuchElementException)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void getTest() {
Optional<User> userOptional = Optional.ofNullable(initUser);
System.out.println(userOptional.get());
Optional<User> nullUserOptional = Optional.ofNullable(null);
System.out.println(nullUserOptional.get());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ifPresent
如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。(为null 则不会进行输出)。
/**
* description Optional ifPresent 如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。(为null 则不会进行输出)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void ifPresentTest() {
Optional<User> userOptional = Optional.ofNullable(initUser);
userOptional.ifPresent(user -> System.out.println(user.getName()));
Optional<User> nullUserOptional = Optional.ofNullable(null);
nullUserOptional.ifPresent(user -> System.out.println(user.getName()));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# orElse
如果存在值,则使用该值调用指定的使用者,否则提供一个调用者指定的值返回。(可以理解为没有值给定一个默认值)。
/**
* description Optional orElse 如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。(为null 则new 一个User)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void orElseTest() {
Optional<User> nullUserOptional = Optional.ofNullable(null);
System.out.println(nullUserOptional.orElse(new User()));
}
2
3
4
5
6
7
8
9
10
11
12
13
# orElseGet
如果存在值,则使用该值调用指定的使用者,否则提供一个调用者指定的值返回。(可以理解为没有值给定一个默认值)。
/**
* description Optional orElseGet 如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。(为null 则new 一个User)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void orElseGetTest() {
Optional<User> nullUserOptional = Optional.ofNullable(null);
System.out.println(nullUserOptional.orElseGet(User::new));
}
2
3
4
5
6
7
8
9
10
11
12
13
orElse和OrElseGet的区别:https://blog.csdn.net/Amandazh/article/details/117109855 (opens new window)
# orElseThrow
如果存在值,则使用该值调用指定的使用者,否则抛出由调用者创建的异常抛出。(可以理解为没有值抛出异常)。
/**
* description Optional orElseThrow 如果存在值,则使用该值调用指定的使用者,否则抛出由调用者创建的异常抛出。(可以理解为没有值抛出异常)
*
* @param
* @return void
* @author Arnold
* @date 2022/6/7
**/
@Test
public void orElseThrowThrow() {
Optional<User> userOptional = Optional.ofNullable(initUser);
System.out.println(userOptional.orElseThrow(RuntimeException::new));
Optional<User> nullUserOptional = Optional.ofNullable(null);
nullUserOptional.orElseThrow(RuntimeException::new);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 其他的一些操作
方法名称 | 作用 |
---|---|
filter | 过滤操作可参考Stream的filter方法 |
flatMap | 过滤操作可参考Stream的flatMap方法 |
isPresent | 判断Optional有值true无值false |
map | 过滤操作可参考Stream的map方法 |
# @FunctionalInterface
只有一个抽象方法的接口我们称之为函数接口。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。
# 特点
- 接口有且仅有一个抽象方法。
- 允许定义静态方法。
- 允许定义默认方法。
- 允许java.lang.Object中的public方法。
- 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
下述所有测试代码源码地址:https://gitee.com/ArnoldSu/functionalProgramming.git (opens new window)
# 自定义函数式接口
package com.bestarnold.www;
/**
* CustomFunctionalInterface 自定义函数式接口
*
* @author Arnold 409196007@qq.com
* @version V2.0
* @date 2022/06/14 12:12
**/
@FunctionalInterface
public interface CustomFunctionalInterface<T> {
/**
* description 抽象方法
*
* @param t 泛型对象
* @return void
* @author Arnold
* @date 2022/6/14
**/
void println(T t);
/**
* description 静态方法打印
*
* @param str 待打印字符串
* @return void
* @author Arnold
* @date 2022/6/14
**/
static void print(String str) {
System.out.print(str);
}
/**
* description 默认方法按照给定次数打印
*
* @param str 打印字符串
* @param times 打印次数
* @return void
* @author Arnold
* @date 2022/6/14
**/
default void printForTimes(String str, Integer times) {
for (int i = 0; i < times; i++) {
System.out.println(str);
}
}
/**
* description java.lang.Object中的public方法
*
* @param obj 比对对象
* @return boolean
* @author Arnold
* @date 2022/6/14
**/
boolean equals(Object obj);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
自定义函数式接口测试
/**
* description 自定义函数式接口测试使用
*
* @param
* @return void
* @author Arnold
* @date 2022/6/14
**/
@Test
public void customFunctionalInterfaceTest() {
//抽象方法
CustomFunctionalInterface<User> customFunctionalInterface =
(User user) -> System.out.println(user.getName());
initUsers.stream().forEach(customFunctionalInterface::println);
//默认方法打印
customFunctionalInterface.printForTimes("默认方法打印字符串", 3);
//静态方法打印
CustomFunctionalInterface.print("静态方法打印字符串");
//继承
System.out.println();
System.out.println(customFunctionalInterface.equals(customFunctionalInterface));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# JDK常见函数式接口
# Supplier
代表结果的提供者(生产者)。不要求每次调用供应商时都返回新的或不同的结果。这是一个功能接口,其功能方法是get() 。
/**
* description supplier 测试
*
* @param
* @return void
* @author Arnold
* @date 2022/6/14
**/
@Test
public void supplierTest() {
Supplier<Date> dateSupplier = Date::new;
System.out.println(dateSupplier.get());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Consumer
表示接受单个输入参数且不返回结果的操作。与大多数其他功能接口不同, Consumer预计将通过副作用进行操作(消费者),其功能方法是accept(Object) 。
/**
* description Consumer 测试
*
* @param
* @return void
* @author Arnold
* @date 2022/6/14
**/
@Test
public void consumerTest() {
initUsers.stream().forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user.getName());
}
});
System.out.println("=============lambda 形式===============");
initUsers.stream().forEach(user -> System.out.println(user.getName()));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Predicate
表示一个参数的谓词(布尔值函数)。这是一个功能接口,其功能方法是test(Object)。
/**
* description Predicate 复合条件防范
*
* @param list 待判断数据
* @param predicate1 判断条件1
* @param predicate2 判断条件2
* @return void
* @author Arnold
* @date 2022/6/15
**/
public static void conditionFilter(List<User> list, Predicate<User> predicate1, Predicate<User> predicate2) {
list.forEach((user) -> {
if (predicate1.and(predicate2).test(user)) {
System.out.println(user);
}
});
}
/**
* description Predicate 测试
*
* @param
* @return void
* @author Arnold
* @date 2022/6/14
**/
@Test
public void predicateTest() {
Boolean flag = initUsers.stream().anyMatch(new Predicate<User>() {
@Override
public boolean test(User user) {
return user.getAge() > 88;
}
});
System.out.println("年龄大于88的人");
System.out.println(flag);
System.out.println("=============lambda 形式===============");
Boolean flag1 = initUsers.stream().anyMatch(user -> user.getAge() > 88);
System.out.println("年龄大于88的人");
System.out.println(flag1);
System.out.println("=============Predicate 复合条件(年龄大于88并且事男性的人员信息)===============");
//Predicate 复合条件(年龄大于88并且事男性的人员信息)
conditionFilter(initUsers, user -> user.getAge() > 88, user -> user.getSex() == 1);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Function
表示接受一个参数并产生结果的函数。其功能方法是apply(Object) 。
/**
* description Function 测试
*
* @param
* @return void
* @author Arnold
* @date 2022/6/14
**/
@Test
public void functionTest() {
List<Optional<List<Department>>> oldManList = initUsers.stream().map(new Function<User, Optional<List<Department>>>() {
@Override
public Optional<List<Department>> apply(User user) {
return user.getSex() == 1 ? Optional.ofNullable(user.getDep()) : Optional.empty();
}
}).collect(Collectors.toList());
oldManList.forEach((optional) -> optional.ifPresent(departments -> System.out.println(departments)));
System.out.println("=============lambda 形式===============");
List<Optional<List<Department>>> oldManList1 = initUsers.stream().map((Function<User, Optional<List<Department>>>) user
-> user.getSex() == 1 ? Optional.ofNullable(user.getDep()) : Optional.empty()).collect(Collectors.toList());
oldManList1.forEach((optional) -> optional.ifPresent(departments -> System.out.println(departments)));
System.out.println("=============Function 复合条件(年龄加十岁昵称加一个前缀Function)===============");
//Predicate 复合条件(年龄大于88并且事男性的人员信息)
initUsers.stream().limit(1).map(new Function<User, User>() {
@Override
public User apply(User user) {
return user.setAge(user.getAge() + 10);
}
}.compose(new Function<User, User>() {
@Override
public User apply(User user) {
return user.setNickName("Function" + user.getNickName());
}
})).collect(Collectors.toList()).forEach(user -> System.out.println(user));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36