ZB-031-java8函数式编程01

为什么要使用函数式编程

  • 减少工作量
  • 提高效率
  • 减少bug

条件筛选用户实例

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
46
47
48
49
50
51
52
53
54
55
public class User {
/** 用户ID,数据库主键,全局唯一 */
private final Integer id;

/** 用户名 */
private final String name;

public User(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public String getName() {
return name;
}

// 过滤ID为偶数的用户
public static List<User> filterUsersWithEvenId(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.id % 2 == 0) {
results.add(user);
}
}
return results;
}

// 过滤姓张的用户
public static List<User> filterZhangUsers(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.name.startsWith("张")) {
results.add(user);
}
}
return results;
}

// 过滤姓王的用户
public static List<User> filterWangUsers(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.name.startsWith("王")) {
results.add(user);
}
}
return results;
}
// 你可以发现,在上面三个函数中包含大量的重复代码。
public static List<User> filter(List<User> users, Predicate<User> predicate) {}
}

可以看到非常麻烦!!!代码非常的重复

先用接口来实现这个功能

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
public class User {
// ...
private interface 判断条件是否成立 {
boolean 这个用户是否满足条件(User user);
}

private static class 用户ID是偶数的条件 implements 判断条件是否成立{
@Override
public boolean 这个用户是否满足条件(User user) {
return user.id % 2 == 0;
}
}

public static List<User> filter(List<User> users, 判断条件是否成立 条件) {
List<User> results = new ArrayList<>();
for (User user: users) {
if(条件.这个用户是否满足条件(user)){
results.add(user);
}
}
return results;
}

public static void main(String[] args) {
List<User> res0 = filter(Arrays.asList(new User(1,"a"),new User(2,"b")),new 用户ID是偶数的条件());
System.out.println(res0.get(0).id);
}
}

不同条件都去实现一个类太麻烦

简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<User> res0 = filter(Arrays.asList(new User(1,"a"),new User(2,"b")),new 用户ID是偶数的条件());
System.out.println(res0.get(0).id);

// 简化——匿名类实现接口
List<User> res1 = filter(Arrays.asList(new User(1,"a"),new User(2,"b")),new 判断条件是否成立(){
@Override
public boolean 这个用户是否满足条件(User user) {
return user.id % 2 == 0;
}
});
System.out.println(res1.get(0).id);

// 再次简化—— lambda 表达式
List<User> res2 = filter(Arrays.asList(new User(1,"a"),new User(2,"b")), user -> user.id % 2 == 0);
System.out.println(res2.get(0).id);

Predicate

回忆高中数学的函数概念y = f(x)

从 x 到 y 的映射

1
2
3
4
5
6
public interface Predicate<T> {
// 将其他类型返回一个 boolean 类型的结果
boolean test(T t);

...
}

Predicate来简化

  • test方法实际就是 User 到 boolean 的映射
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class User {
// ...
public static List<User> filter(List<User> users, Predicate<User> predicate) {
List<User> results = new ArrayList<>();
for (User user: users) {
if(predicate.test(user)){
results.add(user);
}
}
return results;
}

public static void main(String[] args) {
List<User> res = filterUsersWithEvenId(Arrays.asList(new User(1,"a"),new User(2,"b")));
System.out.println(res);

List<User> res2 = filterZhangUsers(Arrays.asList(new User(1,"张三"),new User(2,"李四")));
System.out.println(res2);
}

// 过滤ID为偶数的用户
public static List<User> filterUsersWithEvenId(List<User> users) {
return filter(users, new Predicate<User>() {
@Override
public boolean test(User user) {
return user.id % 2 == 0 ;
}
});
}

public static List<User> filterUsersWithEvenId2(List<User> users) {
return filter(users, user -> user.id % 2 == 0);
}

// 奇怪的语法,因为User.userWithEvenId 是一个静态方法
public static List<User> filterUsersWithEvenId3(List<User> users) {
return filter(users, User::userWithEvenId);
}

// 静态方法 不和实例相绑定
public static boolean userWithEvenId(User user){
return user.getId() % 2 == 0;
}

// 过滤姓张的用户 lambda 表达式
public static List<User> filterZhangUsers(List<User> users) {
return filter(users, user -> user.name.startsWith("张"));
}


public boolean isWang(){
return this.name.startsWith("王");
}

// 过滤姓王的用户
public static List<User> filterWangUsers(List<User> users) {
return filter(users, user -> user.name.startsWith("王"));
}

// 实例方法 默认传递了 this
public static List<User> filterWangUsers2(List<User> users) {
return filter(users, User::isWang);
}
}

lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static List<User> filterUsersWithEvenId(List<User> users) {
return filter(users, new Predicate<User>() {
@Override
public boolean test(User user) {
return user.id % 2 == 0 ;
}
});
}
// 这里的 user -> user.id % 2 == 0 就是 lambda表达式
// 实际是这样 (User user) -> user.id % 2 == 0
// 当只有一个参数的时候 它有类型推断 你可以直接省略为如下格式
public static List<User> filterUsersWithEvenId(List<User> users) {
return filter(users, user -> user.id % 2 == 0);
}

为什么这里可以把 Predicate 简化为 lambda表达式呢?

  • 说白了 test 就是一个函数
  • 任何满足满足从 T 到 boolean 值的映射 都可以自动被转换为一个函数接口

什么是函数接口?

  • Predicate 源码上有一个注解@FunctionInterface 它只是一个标记,不是必要的即使去掉也可以正常工作

三种方式 Predicate

  • lambda 表达式
    • user -> user.name.startsWith("王")
  • static 方法

    1
    2
    3
    static boolean xx(User user){...}

    User::xx
  • 实例方法 instance method

    1
    2
    3
    boolean xxx(){}

    User::xxx

函数接口详解

什么东西可以自动被转换为函数接口呢?

  • 任何只包含一个抽象方法的接口都可以被自动转换为函数接口
    • Predicate 只有一个 test 是抽象方法,其他都有方法体
    • 如我们自己实现一个
      1
      2
      3
      interface 阿猫阿狗 {
      boolean 吃骨头(Animal animal);
      }

Consumer

输入是个 User 输出是一个 void(虚空)

1
2
3
4
5
6
7
8
9
10
11
12
List<User> users = Arrays.asList(new User(1,"a"),new User(2,"b")); 
// Consumer
users.forEach(new Consumer<User>(){
@Override
public void accept(User user){
System.out.println(user);
}
})
// 简化
users.forEach(user -> System.out.println(user));
// 再次简化
users.forEach(System.out::println);

Function (类似js的map函数)

把一个类型User变换成另一个类型String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public String getName() {
// 这里有个隐式的指针 this
return name;
}

public static String mapUserToString(User user, Function<User,String> function){
return function.apply(user);
}

public static void main(String[] args) {
mapUserToString(new User(1,"aaa"),user -> user.getName());
// getName 满足 从 User => String 的映射
mapUserToString(new User(1,"aaa"), User::getName);
}

Supplier

Consumer 的逆操作

  • Consumer 是把一个东西吃掉 消费掉
  • Supplier 是从虚空变成一个东西

从 void ===> Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
// 从虚空 变出一个对象
create(()->new Object());
// alt+ enter
create(Object::new);

// 从虚空 变出一个对象
create(()->"");
// 从虚空 变出一个对象
create(()->new User(1,"abc"));

}

public static Object create(Supplier<Object> supplier){
return supplier.get();
}

其他Function。。。

  • BiConsumer (一次吃两个)
  • BiFunction (两个东西映射成一个东西)
  • BinaryOperator (二元操作符)
  • BiPredicate
  • BooleanSupplier 从boolean 变出一个东西
  • ToIntBiFunction
  • UnaryOperator
  • 。。。

函数式实践

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.github.hcsp.polymorphism;

import java.io.IOException;
import java.util.*;

public class Point implements Comparable<Point>{

private final int x;
private final int y;
// 代表笛卡尔坐标系中的一个点
public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public int getY() {
return y;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

Point point = (Point) o;

if (x != point.x) {
return false;
}
return y == point.y;
}

@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}

@Override
public String toString() {
return String.format("(%d,%d)", x, y);
}

// 按照先x再y,从小到大的顺序排序
// 例如排序后的结果应该是 (-1, 1) (1, -1) (2, -1) (2, 0) (2, 1)
public static List<Point> sort(List<Point> points) {
Collections.sort(points);
return points;
}

public static void main(String[] args) throws IOException {
List<Point> points =
Arrays.asList(
new Point(2, 0),
new Point(-1, 1),
new Point(1, -1),
new Point(2, 1),
new Point(2, -1));
System.out.println(Point.sort(points));


// 传统方式 字节写实现比较器细节
Collections.sort(points, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
if(o1.x < o2.x){
return 1;
}else if (o1.x > o2.x){
return -1;
}

if(o1.y <o2.y){
return 1;
}else if (o1.y > o2.y){
return -1;
}

return 0;
}
});

// java8之后
// java8之后
// java8之后

// 先 x 后 y
Collections.sort(points,Comparator.comparing(Point::getX).thenComparing(Point::getY));

// 先 x 反序 后 y
Collections.sort(points,Comparator.comparing(Point::getX).reversed().thenComparing(Point::getY));

//先 x 反序 后 y 反序
Collections.sort(points,Comparator.comparing(Point::getX).
reversed()
.thenComparing(Comparator.comparing(Point::getY).reversed()));
}

@Override
public int compareTo(Point that) {
// 比较 this 和 that
if(this.x < that.x){
return -1;
}else if(this.x > that.x){
return 1;
}

// 运行到此说明 this.x = that.x
// 因此我要可以进行 y 的排序
if(this.y < that.y){
return -1;
}else if(this.y > that.y){
return 1;
}

return 0;
}
}
  • 详解
1
2
3
4
5
6
7
8
9
// Comparator.comparing 
// 接受一个 Function 参数 从 T类型 到 U类型的映射
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

练习

参考链接