zoukankan      html  css  js  c++  java
  • 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的优化品。

  • 相关阅读:
    Openstack Nova 源码分析 — Create instances (nova-conductor阶段)
    openstack nova 源码解析 — Nova API 执行过程从(novaclient到Action)
    Fiddler抓包9-保存会话(save)
    Fiddler抓包10-会话框添加查看get与post请求类型
    Fiddler抓包11-HTTPS证书Actions无法导出问题
    Fiddler抓包7-post请求(json)
    Fiddler抓包8-打断点(bpu)
    Fiddler抓包3-查看get与post请求
    Fiddler抓包4-工具介绍(request和response)
    Fiddler抓包5-接口测试(Composer)
  • 原文地址:https://www.cnblogs.com/lk625/p/14175721.html
Copyright © 2011-2022 走看看