zoukankan      html  css  js  c++  java
  • java8知识总结_2.方法引用

    2.方法引用

    在正式讲解「方法引用」技术点前,我们先回顾下lambda表达式的基本用法。

    首先lambda表达式的基本用途是用来实现函数式接口的方法。

    这边文件中要用到以下两个java文件,我们这里先把这两个java文件建好。

    文件1:Studen类,该类包含name和age两个属性,一个无参构造函数,一个有参构造函数(初始化name和age);

                 两个属性的get/set方法,一个静态方法,一个实例方法,这两个方法都是用来比较学生的年龄。

    具体请看下面的代码:

     1 public class Student {
     2 
     3     // 姓名
     4     private String name;
     5     // 年龄
     6     private int age;
     7 
     8     /**
     9      * 无参构造器
    10      */
    11     public Student() {
    12 
    13     }
    14 
    15     /**
    16      * 有参构造器_初始化学生属性
    17      * 
    18      * @param name
    19      *            姓名
    20      * @param age
    21      *            年龄
    22      */
    23     public Student(String name, int age) {
    24         this.name = name;
    25         this.age = age;
    26     }
    27 
    28     public String getName() {
    29         return name;
    30     }
    31 
    32     // ###### get/set方法 START ######
    33     public void setName(String name) {
    34         this.name = name;
    35     }
    36 
    37     public int getAge() {
    38         return age;
    39     }
    40 
    41     public void setAge(int age) {
    42         this.age = age;
    43     }
    44     // ###### get/set方法 END ######
    45 
    46     /**
    47      * 静态方法_比较两个学生的年龄
    48      * 
    49      * @param std1
    50      *            学生实例1
    51      * @param std2
    52      *            学生实例2
    53      * @return 等于0   年龄相等
    54      *         大于0   学生实例1的年龄 > 学生实例2的年龄
    55      *         小于0   学生实例1的年龄 < 学生实例2的年龄
    56      */
    57     public static int compareAgeStatic(Student std1, Student std2) {
    58         return std1.getAge() - std2.getAge();
    59     }
    60     
    61     /**
    62      * 普通方法_比较两个学生的年龄
    63      * 
    64      * @param std1
    65      *            学生实例1
    66      * @param std2
    67      *            学生实例2
    68      * @return 等于0   年龄相等
    69      *         大于0   学生实例1的年龄 > 学生实例2的年龄
    70      *         小于0   学生实例1的年龄 < 学生实例2的年龄
    71      */
    72     public int compareAge(Student std1, Student std2) {
    73         return std1.getAge() - std2.getAge();
    74     }
    75 }

    文件2:CompareStudentAge接口,该接口为函数式接口,包含一个抽象方法。

    具体请看下面的代码:

    1 @FunctionalInterface
    2 public interface CompareStudentAge {
    3 
    4     /** 比较两个学生的年龄 */
    5     int compareAge(Student std1,Student std2);
    6 }

    下面我们用之前学习过的lambda表达式,来实现上面接口的compareAge方法。

     1         // lambda表达式实现接口的方法体
     2         CompareStudentAge csa = (Student s1, Student s2) -> {
     3             return s1.getAge() - s2.getAge();
     4         };
     5         
     6         // 声明两个学生实例
     7         Student std1 = new Student("yubx", 36);
     8         Student std2 = new Student("ldm", 35);
     9 
    10         // 调用接口的compareAge方法
    11         int result = csa.compareAge(std1, std2);
    12         
    13         // 打印执行结构
    14         System.out.println(result);

    上面是普通的lambda实现方式,执行结果:1

    基于上面的两个java文件以及对lambda表达式基本写法的回顾,我们来学习java8的另一个特性:「方法引用」

    2-1.概述

    方法引用可以直接引用已有Java类或对象(实例)的方法或构造器。

    方法引用的一般用途是与lambda联合使用。

    方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

    2-2.语法

    类名/对象::静态方法名/实例方法名

    2-3.分类

    1. 类名::静态方法名
    2. 对象::实例方法名
    3. 类名::实例方法名
    4. 类名::new(构造器)

    2-4.实例

    1.类名::静态方法名

    我们用Student类中的静态方法compareAgeStatic来替换lambda的实现。

     1         // 「类名::静态方法名」方法引用
     2         CompareStudentAge csa = Student::compareAgeStatic;
     3         
     4         // 声明两个学生实例
     5         Student std1 = new Student("yubx", 36);
     6         Student std2 = new Student("ldm", 35);
     7 
     8         // 调用接口的compareAge方法
     9         int result = csa.compareAge(std1, std2);
    10         
    11         // 打印执行结构
    12         System.out.println(result);

    执行结果:1

    2.对象::实例方法名

    我们用Student类中的实例方法compareAge来替换lambda的实现。

     1         // 构造学生类
     2         Student stdInstance = new Student();
     3         
     4         // 「对象::实例方法名」
     5         CompareStudentAge csa = stdInstance::compareAge;
     6         
     7         // 声明两个学生实例
     8         Student std1 = new Student("yubx", 36);
     9         Student std2 = new Student("ldm", 35);
    10 
    11         // 调用接口的compareAge方法
    12         int result = csa.compareAge(std1, std2);
    13         
    14         // 打印执行结构
    15         System.out.println(result);

    执行结果:1

    3.类名::实例方法名

    这种类型,对有些小伙伴来说,可能理解起来相对比较难,我尽量说得仔细鞋。

    这里我们要用到this关键字。

    我们首先来看下this关键字的一种用法。

    下面利用JDK的String类中的equals的代码来进行说明。

     1     /**
     2      * Compares this string to the specified object.  The result is {@code
     3      * true} if and only if the argument is not {@code null} and is a {@code
     4      * String} object that represents the same sequence of characters as this
     5      * object.
     6      *
     7      * @param  anObject
     8      *         The object to compare this {@code String} against
     9      *
    10      * @return  {@code true} if the given object represents a {@code String}
    11      *          equivalent to this string, {@code false} otherwise
    12      *
    13      * @see  #compareTo(String)
    14      * @see  #equalsIgnoreCase(String)
    15      */
    16     public boolean equals(Object anObject) {
    17         if (this == anObject) {
    18             return true;
    19         }
    20         if (anObject instanceof String) {
    21             String anotherString = (String)anObject;
    22             int n = value.length;
    23             if (n == anotherString.value.length) {
    24                 char v1[] = value;
    25                 char v2[] = anotherString.value;
    26                 int i = 0;
    27                 while (n-- != 0) {
    28                     if (v1[i] != v2[i])
    29                         return false;
    30                     i++;
    31                 }
    32                 return true;
    33             }
    34         }
    35         return false;
    36     }

    问题:先看第17行的this关键字,这个this代表的是哪个实例?

    想想我们平时调用equals方法的写法:

    1         String st1 = "abc";
    2         String st2 = "bcd";
    3         boolean ret = st1.equals(st2);

    猜想:通过上面的代码,我们大致可以猜到:JDK中的equals方法中的this指代的可能是上记代码中的str1。

    结论:java中对于实例方法,「this引用」隐式的作为第一个参数传递进去,并且默认用这个this调用该实例方法。

    (这种调用方式和python的方法调用很像,有兴趣的小伙伴可以去查阅下相关资料。)

    方法引用就利用了上面的原理,可以实现「类名::实例方法名」的方式。

    综上,我们在上面的Studen类中追加如下实例方法:

        /**
         * 普通方法_比较两个学生的年龄
         * 
         * @param std
         *            学生实例
         * @return 等于0   年龄相等
         *         大于0   学生实例1的年龄 > 学生实例2的年龄
         *         小于0   学生实例1的年龄 < 学生实例2的年龄
         */
        public int compareAge(Student std) {
            return this.getAge() - std.getAge();
        }

    上面的this关键字指代的就是方法接口中的第一个参数。

    这时,我们用「类名::实例方法名」的方式,重写lambda的实现。

     1         // 「类名::实例方法名」
     2         CompareStudentAge csa = Student::compareAge;
     3         
     4         // 声明两个学生实例
     5         Student std1 = new Student("yubx", 36);
     6         Student std2 = new Student("ldm", 35);
     7         
     8         // 调用接口的compareAge方法
     9         int result = csa.compareAge(std1, std2);
    10         
    11         // 打印执行结构
    12         System.out.println(result);

    上面的代码,实际上用的就是std1去调用只有一个参数的实例方法compareAge。

    执行结果:1

    注:如果小伙伴们对上面的代码处理逻辑,理解上还是不够清晰的话,

    建议自己多动手去写些传统代码,加深对this隐式传递处理方式的理解。

    4.类名::new(构造器)

    首先我们假想一个情景:

    ・有一个函数式接口,该接口的抽象方法接受学生姓名和学生你年龄做参数。

    (和Student类中有参构造函数的参数列表一致)

    ・在Student类中追加一个实例方法,该方法接受一个默认this实例引用参数和一个int类型参数(要增加的年龄)

        

    1 @FunctionalInterface
    2 public interface ModifyStudent {
    3 
    4     /** 修改学生的年龄 */
    5     Student modify(String str,int i);
    6 }
     1     /**
     2      * 修改yubx的年龄
     3      * 
     4      * @param addAge
     5      *            增加的年龄
     6      * @return 该学生的实例
     7      */
     8     public Student modify(int addAge) {
     9         if (!this.name.equals("yubx")) {
    10             System.out.println("yubx以外的学生不能修改Ta的年龄!");
    11             return this;
    12         }
    13         this.setAge(this.age + addAge);
    14         return this;
    15     }

    基于上面的代码,我们用「类名::new(构造器)」的方式重写lambda的实现。

     1         // 「类名::new(构造函数)」
     2         ModifyStudent student = Student::new;
     3         
     4         // 调用接Student类中的compareAge方法
     5         Student newStd = student.modify("yubx", 36);
     6         // 调用Student类中的modify方法
     7         newStd.modify(10);
     8         
     9         // 打印执行结构
    10         System.out.println("学生"+newStd.getName()+"的年龄是" +newStd.getAge());

    执行结果:学生yubx的年龄是46

    我们把上面第5行的参数列表修改下:

     1         // 「类名::new(构造函数)」
     2         ModifyStudent student = Student::new;
     3         
     4         // 调用接Student类中的compareAge方法
     5 //        Student newStd = student.modify("yubx", 36);
     6         Student newStd = student.modify("ldm", 35);
     7         // 调用Student类中的modify方法
     8         newStd.modify(10);
     9         
    10         // 打印执行结构
    11         System.out.println("学生"+newStd.getName()+"的年龄是" +newStd.getAge());

    执行结果:

    yubx以外的学生不能修改Ta的年龄!

    学生ldm的年龄是35

    2-5.综上小结

    上面我们用具体实例结合lambda表达式,学习了方法引用的四种方式,从中我们总结一个结论:

    使用「方法引用」时,要引用的方法(也就是操作符「::」后的方法)的参数列表,

    必须要与lambda表达式要实现的函数式接口的方法参数列表一致。

    说到底,lambda表达式的住哟用途:实现函数式接口的抽象方法。

    而「方法引用」就是用来替换lambda表达式的"方法体"(具体实现)。

    以上,希望还没理解吃透的小伙伴,查找以下其他资料,多多练习。如果有心得,欢迎底下留言交流!

    PS:想应用实例或应用场景太费脑细胞~~!

  • 相关阅读:
    十天冲刺之三
    设计模式-模板方法模式
    设计模式-观察者模式
    设计模式-迭代子模式
    设计模式-责任链模式
    设计模式-门面模式
    1395. Count Number of Teams
    747. Largest Number At Least Twice of Others
    1160. Find Words That Can Be Formed by Characters
    1539. Kth Missing Positive Number
  • 原文地址:https://www.cnblogs.com/yubx/p/12536901.html
Copyright © 2011-2022 走看看