Java基础(二十七)------Lambda表达式优化之方法引用
方法引用
在使用Lambda表达式的时候,实际上我们传递的是一段解决问题的代码,给什么参数做什么操作。
Lambda冗余的场景
1 //准备一个函数式接口
2 @FunctionalInterface
3 public interface Printable {
4 //定义唯一的抽象方法
5 void print(String str);
6 }
准备一个测试类
1 public class Demo01Method {
2 //定义一个静态的方法,方法的参数传递一个函数式接口
3 public static void printString(Printable p){
4 p.print("Hello World");
5 }
6 public static void main(String[] args) {
7 //printString(str->System.out.println(str.toUpperCase()));//Hello World--->HELLO WORLD
8 //传统的Lambda表达式
9 printString((String str)->{
10 Demo02Method method = new Demo02Method();
11 method. printUpperCaseString(str);
12 });
13 //打印流对象已经确定
14 PrintStream printStream = System.out;
15 //通过对象来引用对应的成员方法
16 printString( printStream::println);
17 /*
18 使用方法引用优化Lambda
19 1. 对象已经是存在的method
20 2.成员方法也是已经存在的 printUpperCaseString
21 所以我们就可以使用对象名来引用成员方法
22 */
23 //首先必须是对象已经存在
24 Demo02Method method = new Demo02Method();
25 printString(method::printUpperCaseString);
26 }
27 }
在测试类中,定义一个静态方法,静态方法传递一个函数式接口Printable,函数式接口当中定义了唯一一个抽象方法print,这个print方法接收一个字符串参数。,目的就是为了打印接收的字符串参数,通常我们可以使用Lambda表达式来实现以上需求。
//打印流对象已经确定
PrintStream printStream = System.out;
//通过对象来引用对应的成员方法
printString( printStream::println);
双冒号::也被归置为引用运算符。
使用方法引用的使用场景
通过对象名引用成员方法
1 //先准备一个类,类中需要定义一个成员方法
2 public class Demo02Method {
3 // 定义一个成员方法,传递一个字符串,把字符串转换为大写输出
4 public void printUpperCaseString(String str) {
5 System.out.println(str.toUpperCase());
6 }
7 }
8 //准备一个函数式接口
9 @FunctionalInterface
10 public interface Printable {
11 //定义唯一的抽象方法
12 void print(String str);
13 }
14 public class Demo01Method {
15
16 // 定义一个静态的方法,方法的参数传递一个函数式接口
17 public static void printString(Printable p) {
18 p.print("Hello World");
19 }
20 public static void main(String[] args) {
21 /*
22 * 使用方法引用优化Lambda 1. 对象已经是存在的method 2.成员方法也是已经存在的 printUpperCaseString
23 * 所以我们就可以使用对象名来引用成员方法
24 */
25 // 首先必须是对象已经存在
26 Demo02Method method = new Demo02Method();
27 printString(method::printUpperCaseString);
28 }
29 }
通过类名引用静态方法
比如:java.lang.Math
类中存放的都是静态方法
1 //首先定义一个函数式接口
2 @FunctionalInterface
3 public interface Demo01MathStaticMethod {
4 //定义抽象方法
5 double calculateAbs(double d);
6 }
7 //定义一个测试类
8 public class Demo02MethodStatic {
9 // 定义一个静态方法,该方法中传递一个函数式接口
10 public static double calc(double d, Demo01MathStaticMethod math) {
11 return math.calculateAbs(d);
12 }
13 public static void main(String[] args) {
14 // 传统的Lambda表达式写法
15 double calc = calc(-3.14, (d) -> {
16 return Math.abs(d);
17 });
18 System.out.println(calc);
19 /*
20 * 使用方法引用进行优化Lambda
21 首先类名已经确定的
22 类中定义的静态方法式已经确定的
23 使用类名引用类中的静态方法
24 */
25 double d = calc(-3.14, Math::abs);
26 System.out.println(d);// 3.14
27 }
28 }
Lambda表达式写法:d->Math.abds(d)
方法引用写法:Math::abs
这两种写法是等价的。
通过super来引用成员方法
1 //定义一个父类
2 public class Animal {
3 //定义一个成员方法 交流的方法
4 public void talk() {
5 System.out.println("hello 我是一只动物!");
6 }
7 }
8 //定义一个函数式接口
9 @FunctionalInterface
10 public interface Meet {
11 //定义一个抽象方法见面的方法
12 void meet();
13 }
14 //定义一个子类
15 public class Cat extends Animal {
16 @Override
17 public void talk() {
18 System.out.println("hello 我是一只猫!");
19 }
20 //定义一个方法 方法的参数传递一个函数式接口
21 public void meet(Meet m) {
22 m.meet();
23 }
24 //定义一个成员方法 沟通的方法
25 public void commun() {
26 //传统的Lambda表达式写法
27 meet( ()->{
28 //创建父类的对象
29 //调用父类的方法
30 Animal animal = new Animal();
31 animal.talk();
32 });
33
34 //使用父类当中的方法 直接用super来调用
35 meet( ()->super.talk());
36 /*
37 使用super关键字来引用成员方法
38 super已经存在的
39 父类当中的成员方法talk已经存在的
40 可以使用super引用父类当中的成员方法
41 */
42 meet(super::talk);
43 }
44 public static void main(String[] args) {
45 new Cat().commun();
46 }
47 }
this指代当前对象,如果需要引用的方法就是本类当中的成员方法,那么可以使用this::成员方法
1 //定义一个学生类
2 public class Student {
3
4 // 定义一个成员方法,方法的参数传递一个函数式接口Study
5 public void study(Study s) {
6 s.study();
7 }
8
9 // 定义一个work方法
10 public void work() {
11 System.out.println("我学习我快乐!");
12 }
13
14 // 定义一个成员方法 快乐的方法
15 public void toHappy() {
16 // 传统的Lambda表达式
17 study(() -> {
18 // 创建对象
19 Student student = new Student();
20 student.work();
21 });
22 // 使用this关键字优化Lambda
23 //Student student = new Student();
24 //study(student::work);
25 study(this::work);
26 }
27
28 public static void main(String[] args) {
29 new Student().toHappy();
30 }
31 }
类的构造器引用
1 public class Person {
2 private String name;
3
4 public String getName() {
5 return name;
6 }
7
8 public void setName(String name) {
9 this.name = name;
10 }
11
12 public Person(String name) {
13 this.name = name;
14 }
15
16 public Person() {
17 }
18
19 @Override
20 public String toString() {
21 return "Person [name=" + name + "]";
22 }
23 }
24
25 @FunctionalInterface
26 public interface PersonCreate {
27 Person createPerson(String name);
28 }
29
30 public class TestConstructorMethod {
31 public static void printPersonName(String name,PersonCreate create) {
32 System.out.println(create.createPerson(name).getName());
33 }
34 public static void main(String[] args) {
35 //使用传统的Lambda表达式
36 //printPersonName("小孙", name->new Person(name));
37
38 //使用构造器来优化Lambda表达式
39 printPersonName("小孙", Person::new);
40 /*
41 Lambda表达式
42 name -> new Person(name)
43 方法引用:Person::new
44 */
45 }
46 }
数组也是Object的子类对象,所以同样具有构造器,只不过语法稍微不同
1 //定义一个函数式接口
2 @FunctionalInterface
3 public interface BuildArrays {
4 //定义唯一的抽象方法
5 public abstract int[] buildArrays(int length);
6 }
7
8 //定义
9 public class Demo01ArraysConstructorMethod {
10
11 //定义一个方法方法中传递一个函数式接口,还有传递一个数组的长度
12 public static int[] buildArrays(int length,BuildArrays buildArrays) {
13 return buildArrays.buildArrays(length);
14 }
15 public static void main(String[] args) {
16 //先用Lambda表达式来操作
17 int[] arr01 = buildArrays(10, length-> new int[length]);
18 System.out.println(arr01.length);//10
19
20 //数组的构造器引用来优化Lambda表达式
21 int[] arr02 = buildArrays(10,int[]::new);
22 System.out.println(arr02.length);//10
23 /*
24 *Lambda表达式: length -> new int[length]
25 *方法引用: int[]::new
26 *这两种写法是等价的
27 */
28 }
29 }
为什么可以使用这样的方式优化Lambda表达式?
如果使用Lambda,那么根据可推导就可以省略原则,无需指定参数类型,也无需指定的重写的形式—>它们都可以被推导出来,所以就可以省略掉。能够使用方法引用,同样也是可以根据上下文进行推导。
函数式接口是Lambda表达式的基础,而方法引用是Lambda的优化品。