读书笔记——《Java 8实战》系列之方法引用

来源:互联网 发布:js获取当前时间字符串 编辑:程序博客网 时间:2024/05/24 07:00

转自 http://www.wxueyuan.com/blog/articles/2017/10/19/1508379046077.html

在上一篇博客中,我们继续介绍了一些关于Lambda表达式的相关知识。在本篇博客中,我将向大家介绍一种通过调用特定方法来使Lambda表达式更简捷的办法——方法引用。事实上,方法引用就是让你根据已有的方法实现来创建Lambda表达式。

简单来说,方法引用的语法就是:

目标引用::方法名称

需要注意的是这里的方法名称后面不加圆括号(),因为我们并没有实际调用这个方法

方法引用大概分为三类:

类型方法引用示例指向静态方法的方法引用Class::staticMethod(如 Integer::parseInt)指向任意类型实例方法的方法引用Class::instanceMethod(如 String::length)指向现有对象的实例方法的方法引用object::instanceMethod(如 student::getHeight)

第二种方法引用与第三种方法引用乍一看会让人有些迷惑,实际上第二种方法引用主要是用在当我们Lambda表达式的主体中调用参数对象的某个方法时,如:

    (String s) -> s.length()     可以改写成     String::length

而第三种方法引用主要时用在当我们在调用一个外部对象的方法时,如:

    () -> student.getHeight()    可以改写成      student::getHeight

下图是为三种不同类型的Lambda表达式构建方法引用的办法

e4ebadd76a1247e5a12993f2fadbef18.png

接下来我就用一个例子——给学生利用不同策略来排序为大家总结一下,行为参数化,Lambda表达式(一),Lambda表达式(二),和本篇博客的重要内容。

  • 首先是我们的Student实体类
    class Student{        private String name;           //学生姓名        private Integer avgScore;          //平均成绩        private Integer height;            //学生身高        public Student(String name, Integer avgScore, Integer height) {            super();            this.name = name;            this.avgScore = avgScore;            this.height = height;        }        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public Integer getAvgScore() {            return avgScore;        }        public void setAvgScore(int avgScore) {            this.avgScore = avgScore;        }        public Integer getHeight() {            return height;        }        public void setHeight(int height) {            this.height = height;        }        @Override        public String toString() {            return this.getName();        }    }
  • Java 的API类库中已经提供了List的sort方法,我们不用去自己实现它,只需要传入一个Comparator类型的对象,list就可以按照它的规则来排序
    void sort(Comparator<? super E> c)
  • Comparator是一个函数式接口,它提供了一个比较两个对象大小的方法compare(T o1, T o2),返回一个int类型的值,该返回值可能为负数,0,或者正数,分别对应着o1小于,等于,或者大于o2。
    //返回值可能为负数,0,或者正数,分别对应着o1小于,等于,或者大于o2    int compare(T o1, T o2);
  • 要想将一个List中的学生以某种规则排序,我们以行为参数化的角度来考虑,第一种解决方案应该如下:
    //实现一个Comparator接口的实体类StudentHeightComparator,通过比较两个学生的身高,来比较两个student对象的大小    class StudentHeightComparator implements Comparator<Student>{    @Override    public int compare(Student s1, Student s2) {        // TODO Auto-generated method stub        return s1.getHeight().compareTo(s2.getHeight());    }    public static void main(String[] args) {        // TODO Auto-generated method stub        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        //实例化StudentHeightComparator,并将其作为参数传入到sort方法中去        students.sort(new StudentHeightComparator());        System.out.println(students.toString());    }}
  • 匿名类的机制可以一定程度上优化我们的第一种解决方案,它的解决方案应该如下:
    public static void main(String[] args) {        // TODO Auto-generated method stub        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        //使用匿名类,我们就无需实现一个只需实例化一次的类StudentHeightComparator        students.sort(new Comparator<Student>() {            @Override            public int compare(Student s1, Student s2) {                // TODO Auto-generated method stub                return s1.getHeight().compareTo(s2.getHeight());            }        });        System.out.println(students.toString());    }
  • 尽管匿名类一定程度上减少了方案一中啰嗦的代码,但我们可以使用Lambda表达式来使方案二变得更加简单:
    public static void main(String[] args) {        // TODO Auto-generated method stub        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        //Comparator实际上代表了(T,T) -> int 的函数描述符        students.sort((s1,s2) -> s1.getHeight().compareTo(s2.getHeight()));        System.out.println(students.toString());    }
  • 现在我们来为实体类Student增加一个静态方法,来写出一个不一样的Lambda表达式:
    class Student{        ......        public static int compareStudentByHeight(Student s1, Student s2) {            return s1.getHeight().compareTo(s2.getHeight());        }    }    public static void main(String[] args) {        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        //使用Student类的静态方法        students.sort((s1,s2) -> Student.compareStudentByHeight(s1,s2));        System.out.println(students.toString());    }
  • 最后,我们使用方法引用来将上面的代码变得更加简洁,使得代码阅读起来就像问题描述的一样:
    public static void main(String[] args) {        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        //使用Student类的静态方法的方法引用        students.sort(Student::compareStudentByHeight);        System.out.println(students.toString());    }
  • 最后的这种解决方案呢,其实是用了我们上面提到的指向静态方法的方法引用,那么同学们能不能自己写出剩下的两种类型的方法引用呢?

指向任意类型实例方法的方法引用实例

    class Student{        ......        //一个普通的实例方法        public int compareToStudentByHeight(Student s) {            return this.getHeight().compareTo(s.getHeight());        }    }    public static void main(String[] args) {        // TODO Auto-generated method stub        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        //使用任意类型对象实例方法的Lambda表达式形式,等价于下一行代码        students.sort((s1,s2) -> s1.compareToStudentByHeight(s2));        //使用指向任意类型对象实例方法的方法引用,进一步简化了上一行代码        students.sort(Student::compareToStudentByHeight);        System.out.println(students.toString());    }

指向现有对象的实例方法的方法引用

    public static void main(String[] args) {        // TODO Auto-generated method stub        List<Student> students = new ArrayList<>();        students.add(new Student("a",90,180));        students.add(new Student("b",80,175));        students.add(new Student("c",70,190));        StudentComparatorProvider provider = new StudentComparatorProvider();        //使用指向现有对象的实例方法的Lambda表达式形式,等价于下一行代码        students.sort((s1,s2) -> provider.compareStudentByHeight(s1, s2));        //使用指向现有对象的实例方法的方法引用,进一步简化了上一行代码        students.sort(provider::compareStudentByHeight);        System.out.println(students.toString());    }
  • 除此之外,对于一个现有的构造函数,你可以利用它的名称和关键字new来创建一个它的引用,首先我们先稍微修改下我们的Student实体类,在实体类中分别提供无参,一个参数和两个参数的构造函数。
    class Student{        private String name;           //学生姓名        private Integer avgScore;          //平均成绩        public Student() {            this.name = "defaultName";            this.avgScore = 0;        }        public Student(String name) {            this.name = name;            this.avgScore = 0;        }        public Student(String name, Integer avgScore) {            this.name = name;            this.avgScore = avgScore;        }        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public Integer getAvgScore() {            return avgScore;        }        public void setAvgScore(int avgScore) {            this.avgScore = avgScore;        }        @Override        public String toString() {            return "姓名:"+this.getName()+" 平均分: "+this.getAvgScore();        }    }
  • 假如使用new Student()构造函数,它的函数签名是() -> Student, 符合Supplier 的() -> T 的函数签名,因此我们可以用以下几种不同的方法创建一个Student实例:
    public static void main(String[] args) {        //使用无参构造函数创建实例        Student student1 = new Student();        System.out.println(student1.toString());        //使用Lambda表达式创造实例,等价于下面使用方法引用创建实例        Supplier<Student> s1 = () -> new Student();        Student student2 = s1.get();        System.out.println(student2.toString());        //使用方法引用进一步简化Lambda表达式的写法        Supplier<Student> s= Student::new;        Student student3 = s.get();        System.out.println(student3.toString());    }
  • 假如使用new Student(String name)构造函数,它的函数签名是(String) -> Student,符合Function
    public static void main(String[] args) {        //使用一个参数构造函数创建实例        Student student1 = new Student("Jesmin");        System.out.println(student1.toString());        //使用Lambda表达式创造实例,等价于下面使用方法引用创建实例        Function<String,Student> f = s -> new Student(s);        Student student2 = f.apply("Jesmin");        System.out.println(student2.toString());        //使用方法引用进一步简化Lambda表达式的写法        Function<String,Student> f2= Student::new;        Student student3 = f2.apply("Jesmin");        System.out.println(student3.toString());    }
  • 假如使用new Student(String name, Integer avgScore)构造函数,他的函数签名是(String,Integer) -> Student,符合BiFunction
    public static void main(String[] args) {        //使用两个参数构造函数创建实例        Student student1 = new Student("Jesmin",90);        System.out.println(student1.toString());        //使用Lambda表达式创造实例,等价于下面使用方法引用创建实例        BiFunction<String,Integer,Student> bf = (s,i) -> new Student(s,i);        Student student2 = bf.apply("Jesmin",90);        System.out.println(student2.toString());        //使用方法引用进一步简化Lambda表达式的写法        BiFunction<String,Integer,Student> bf2= Student::new;        Student student3 = bf2.apply("Jesmin",90);        System.out.println(student3.toString());    }
阅读全文
1 0
原创粉丝点击