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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jesministrator/article/details/78402359

转自 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());

    }
阅读更多

没有更多推荐了,返回首页