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表达式来实现以上需求。

  但是经过我们观察,对字符串进行控制台打印输出的操作方案,明明已经有了线程的执行方案,System.out对象中有一个方法println(String str),所以我们可以直接通过对象名来引用该方法println来实现在控制台打印输出字符串内容

//打印流对象已经确定
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来引用成员方法

如果存在继承关系,当Lambda中需要使用super调用时也可以使用方法引用来优化Lambda表达式。

 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指代当前对象,如果需要引用的方法就是本类当中的成员方法,那么可以使用this::成员方法格式来优化Lambda表达式

 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 }

类的构造器引用

由于构造器的名称与类名完全一样,所以构造器的类名称使用类名称::new的格式来表示

 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的优化品。

 

posted @ 2020-12-22 22:05  寒江雨  阅读(236)  评论(0编辑  收藏  举报