Lambda表达式

Lamdba是有特有的格式的,按照下面的格式来编写Lamdba。

1
2
3
(被重写方法的形参列表) -> {
被重写方法的方法体代码;
}

需要给说明一下的是,在使用Lambda表达式之前,必须先有一个接口,而且接口中只能有一个抽象方法。(注意:不能是抽象类,只能是接口)

像这样的接口,我们称之为函数式接口,只有基于函数式接口的匿名内部类才能被Lambda表达式简化。

1
2
3
public interface Swimming{
void swim();
}

有了以上的Swimming接口之后,接下来才能再演示,使用Lambda表达式,简化匿名内部类书写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LambdaTest1 {
public static void main(String[] args) {
// 目标:认识Lambda表达式.
//1.创建一个Swimming接口的匿名内部类对象
Swimming s = new Swimming(){
@Override
public void swim() {
System.out.println("学生快乐的游泳~~~~");
}
};
s.swim();

//2.使用Lambda表达式对Swimming接口的匿名内部类进行简化
Swimming s1 = () -> {
System.out.println("学生快乐的游泳~~~~");
};
s1.swim();
}
}

好的,我们现在已经知道Lamdba表达式可以简化基于函数式接口的匿名内部类的书写。接下来,我们可以把刚才使用Arrays方法时的代码,使用Lambda表达式简化一下了。

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
public class LambdaTest2 {
public static void main(String[] args) {
// 目标:使用Lambda简化函数式接口。
double[] prices = {99.8, 128, 100};
//1.把所有元素*0.8: 先用匿名内部类写法
Arrays.setAll(prices, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
// value = 0 1 2
return prices[value] * 0.8;
}
});
//2.把所有元素*0.8: 改用Lamdba表达式写法
Arrays.setAll(prices, (int value) -> {
return prices[value] * 0.8;
});

System.out.println(Arrays.toString(prices));
System.out.println("-----------------------------------------------");

Student[] students = new Student[4];
students[0] = new Student("Josh", 169.5, 23);
students[1] = new Student("Ben", 163.8, 26);
students[2] = new Student("Ben", 163.8, 26);
students[3] = new Student("Philips", 167.5, 24);
//3.对数组中的元素按照年龄升序排列: 先用匿名内部类写法
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
}
});
//4.对数组中的元素按照年龄升序排列: 改用Lambda写法
Arrays.sort(students, (Student o1, Student o2) -> {
return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
});
System.out.println(Arrays.toString(students));
}
}

Lambda表达式省略规则

我们已经明白Lambda表达式的基本使用。Java觉得代码还不够简单,于是还提供了Lamdba表达式的几种简化写法。具体的简化规则如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.Lambda的标准格式
(参数类型1 参数名1, 参数类型2 参数名2)->{
...方法体的代码...
return 返回值;
}

2.在标准格式的基础上()中的参数类型可以直接省略
(参数名1, 参数名2)->{
...方法体的代码...
return 返回值;
}

3.如果{}总的语句只有一条语句,则{}可以省略、return关键字、以及最后的“;”都可以省略
(参数名1, 参数名2)-> 结果

4.如果()里面只有一个参数,则()可以省略
(参数名)->结果

接下来从匿名内部类开始、到Lambda标准格式、再到Lambda简化格式,一步一步来简化一下。同学们体会一下简化的过程。

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
public class LambdaTest2 {
public static void main(String[] args) {
// 目标:使用Lambda简化函数式接口。
double[] prices = {99.8, 128, 100};
//1.对数组中的每一个元素*0.8: 匿名内部类写法
Arrays.setAll(prices, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
// value = 0 1 2
return prices[value] * 0.8;
}
});
//2.需求:对数组中的每一个元素*0.8,使用Lambda表达式标准写法
Arrays.setAll(prices, (int value) -> {
return prices[value] * 0.8;
});
//3.使用Lambda表达式简化格式1——省略参数类型
Arrays.setAll(prices, (value) -> {
return prices[value] * 0.8;
});
//4.使用Lambda表达式简化格式2——省略()
Arrays.setAll(prices, value -> {
return prices[value] * 0.8;
});
//5.使用Lambda表达式简化格式3——省略{}
Arrays.setAll(prices, value -> prices[value] * 0.8 );

System.out.println(Arrays.toString(prices));

System.out.println("------------------------------------

Student[] students = new Student[4];
students[0] = new Student("Josh", 169.5, 23);
students[1] = new Student("Ben", 163.8, 26);
students[2] = new Student("Ben", 163.8, 26);
students[3] = new Student("Philips", 167.5, 24);

//1.使用匿名内部类
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
}
});
//2.使用Lambda表达式表达式——标准格式
Arrays.sort(students, (Student o1, Student o2) -> {
return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
});
//3.使用Lambda表达式表达式——省略参数类型
Arrays.sort(students, ( o1, o2) -> {
return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
});
//4.使用Lambda表达式表达式——省略{}
Arrays.sort(students, ( o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()));


System.out.println(Arrays.toString(students));
}
}

到这里,对Lamdba表达式的所有写法,就学习完毕了。

Lambda表达式的JDK8新特性(方法引用)

各位小伙伴,接下来我们学习JDK8的另一个新特性,叫做方法引用。我们知道Lambda是用来简化匿名代码的书写格式的,而方法引用是用来进一步简化Lambda表达式的,它简化的更加过分。

静态方法引用

我们先学习静态方法的引用,还是用之前Arrays代码来做演示。现在准备好下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Test1 {
public static void main(String[] args) {
Student[] students = new Student[4];
students[0] = new Student("Josh", 169.5, 23);
students[1] = new Student("Ben", 163.8, 26);
students[2] = new Student("Ben", 163.8, 26);
students[3] = new Student("Philips", 167.5, 24);

// 原始写法:对数组中的学生对象,按照年龄升序排序
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge(); // 按照年龄升序排序
}
});

// 使用Lambda简化后的形式
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
}
}

现在,我想要把下图中Lambda表达式的方法体,用一个静态方法代替

1
2
//使用Lambda简化后的形式
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());

准备另外一个类CompareByData类,用于封装Lambda表达式的方法体代码;

1
2
3
4
5
public class CompareByData {
public static int compareByAge(Student o1, Student o2){
return o1.getAge() - o2.getAge(); // 升序排序的规则
}
}

现在我们就可以把Lambda表达式的方法体代码,改为下面的样子

1
Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2));

Java为了简化上面Lambda表达式的写法,利用方法引用可以改进为下面的样子。实际上就是用类名调用方法,但是把参数给省略了。这就是静态方法引用

1
2
//静态方法引用:类名::方法名
Arrays.sort(students, CompareByData::compareByAge);

实例方法引用

还是基于上面的案例,我们现在来学习一下实例方法的引用。现在,我想要把下图中Lambda表达式的方法体,用一个实例方法代替。

1
2
//使用Lambda简化后的形式
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());

在CompareByData类中,再添加一个实例方法,用于封装Lambda表达式的方法体

1
2
3
4
5
6
7
8
public class CompareByDate {
public static int compareByAge(Student o1, Student o2) {
return o1.getAge() - o2.getAge();//升序顺序规则
}
public int compareByAgeDesc(Student o1, Student o2) {
return o2.getAge() - o1.getAge();//升序顺序规则
}
}

接下来,我们把Lambda表达式的方法体,改用对象调用方法

1
2
CompareByData compare = new CompareByData();
Arrays.sort(students, (o1, o2) -> compare.compareByAgeDesc(o1, o2)); // 降序

最后,再将Lambda表达式的方法体,直接改成方法引用写法。实际上就是用类名调用方法,但是省略的参数。这就是实例方法引用

1
2
CompareByData compare = new CompareByData();
Arrays.sort(students, compare::compareByAgeDesc); // 降序

特定类型的方法引用

我们继续学习特定类型的方法引用。在学习之前还是需要给大家说明一下,这种特定类型的方法引用是没有什么道理的,只是语法的一种约定,遇到这种场景,就可以这样用。

Java约定:如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数作为方法的主调,后面的所有参数都是作为该实例方法的入参时,则就可以使用特定类型的方法引用。
格式:类型::方法名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test2 {
public static void main(String[] args) {
String[] names = {"boby", "angela", "Andy" ,"dlei", "caocao", "Babo", "jack", "Cici"};

// 要求忽略首字符大小写进行排序。
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 制定比较规则。o1 = "Andy" o2 = "angela"
return o1.compareToIgnoreCase(o2);
}
});

//lambda表达式写法
Arrays.sort(names, ( o1, o2) -> o1.compareToIgnoreCase(o2) );
//特定类型的方法引用!
Arrays.sort(names, String::compareToIgnoreCase);

System.out.println(Arrays.toString(names));
}
}

构造器引用

我们学习最后一种方法引用的形式,叫做构造器引用。还是先说明一下,构造器引用在实际开发中应用的并不多,目前还没有找到构造器的应用场景。所以大家在学习的时候,也只是关注语法就可以了。

现在,我们准备一个JavaBean类,Car类

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
public class Car {
private String name;
private double price;

public Car() {

}

public Car(String name, double price) {
this.name = name;
this.price = price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}

因为方法引用是基于Lamdba表达式简化的,所以也要按照Lamdba表达式的使用前提来用,需要一个函数式接口,接口中代码的返回值类型是Car类型

1
2
3
interface CreateCar{
Car create(String name, double price);
}

最后,再准备一个测试类,在测试类中创建CreateCar接口的实现类对象,先用匿名内部类创建、再用Lambda表达式创建,最后改用方法引用创建。我们只关注格式就可以,不要去想为什么(语法就是这么设计的)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test3 {
public static void main(String[] args) {
// 1、创建这个接口的匿名内部类对象。
CreateCar cc1 = new CreateCar(){
@Override
public Car create(String name, double price) {
return new Car(name, price);
}
};
//2、使用匿名内部类改进
CreateCar cc2 = (name, price) -> new Car(name, price);

//3、使用方法引用改进:构造器引用
CreateCar cc3 = Car::new;

//注意:以上是创建CreateCar接口实现类对象的几种形式而已,语法一步一步简化。

//4、对象调用方法
Car car = cc3.create("奔驰", 49.9);
System.out.println(car);
}
}

链接

封面图来源:https://www.pixiv.net/artworks/67566958