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) { Swimming s = new Swimming(){ @Override public void swim() { System.out.println("学生快乐的游泳~~~~"); } }; s.swim(); 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) { double[] prices = {99.8, 128, 100}; Arrays.setAll(prices, new IntToDoubleFunction() { @Override public double applyAsDouble(int value) { return prices[value] * 0.8; } }); 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); Arrays.sort(students, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return Double.compare(o1.getHeight(), o2.getHeight()); } }); 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) { double[] prices = {99.8, 128, 100}; Arrays.setAll(prices, new IntToDoubleFunction() { @Override public double applyAsDouble(int value) { return prices[value] * 0.8; } }); Arrays.setAll(prices, (int value) -> { return prices[value] * 0.8; }); Arrays.setAll(prices, (value) -> { return prices[value] * 0.8; }); Arrays.setAll(prices, value -> { return prices[value] * 0.8; }); 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(); } });
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge()); } }
|
现在,我想要把下图中Lambda表达式的方法体,用一个静态方法代替
1 2
| 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
| 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) { return o1.compareToIgnoreCase(o2); } }); 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) { CreateCar cc1 = new CreateCar(){ @Override public Car create(String name, double price) { return new Car(name, price); } }; CreateCar cc2 = (name, price) -> new Car(name, price);
CreateCar cc3 = Car::new; Car car = cc3.create("奔驰", 49.9); System.out.println(car); } }
|
链接
封面图来源:https://www.pixiv.net/artworks/67566958