Java 8 新特性:方法引用

一、什么是方法引用?

当我们在写代码的时候,发现方法的入参是一个函数接口,比如我们在对数组进行排序,需要使用Arrays.sort(T[] a, Comparator<? super T> c)方法,此时Comparator是一个函数接口,在没有lambda的情况下,我们需要提供一个匿名内部类来实现Comparator接口,在Java 8中可以通过lambda表达式来实现,如果我们的代码中已经有一个类实现了Comparator接口,那么是否可以直接使用该类实现的compare方法呢?答案是可以的,这就是方法引用存在的意义,方法引用是用来直接访问类或者实例已经存在的方法或者构造方法,如果某个方法签名和接口恰好一致,就可以直接传入方法引用,恰好一致要求除了方法名外,方法参数一致且返回类型相同。计算时,方法引用会创建函数式接口的一个实例。

二、使用方法引用的好处?

1、方法引用的唯一用途是支持Lambda的简写。

2、方法引用提高了代码的可读性,也使逻辑更加清晰。

三、语法:

1、使用::操作符将方法名类或者实例的名字分隔开

四、分类:

1、类名::静态方法名

2、对象::实例方法名

3、类名::实例方法名

4、类名::new

五、举例:

定义一个Student类:

import java.util.Arrays;
import java.util.List;

public class Student {
    private String name;

    private int score;

    public Student(){

    }

    public Student(String name,int score){
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public static int compareStudentByScore(Student student1,Student student2){
        return student1.getScore() - student2.getScore();
    }
}

现在有一批学生,需要我们按着分数由小到大排列并输出

1、首先我们使用lambda表达式的方式处理:

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",60);
        Student student2 = new Student("lisi",70);
        Student student3 = new Student("wangwu",80);
        Student student4 = new Student("zhaoliu",90);
        List<Student> students = Arrays.asList(student1,student2,student3,student4);
        students.sort((o1, o2) -> o1.getScore() - o2.getScore());
        students.forEach(student -> System.out.println(student.getScore()));
    }

2、【类名::静态方法名】此时我们发现Student类的compareStudentByScore方法与compare方法签名是一致的, 所有我们可以使用方法引用来改写:

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",60);
        Student student2 = new Student("lisi",70);
        Student student3 = new Student("wangwu",80);
        Student student4 = new Student("zhaoliu",90);
        List<Student> students = Arrays.asList(student1,student2,student3,student4);
        students.sort((o1, o2) -> o1.getScore() - o2.getScore());
        students.forEach(student -> System.out.println(student.getScore()));
        students.sort(Student::compareStudentByScore);
        students.forEach(student -> System.out.println(student.getScore()));
    }

3、【对象::实例方法名】我们在Student里面增加一个实例方法,与静态方法一样:

    public int compareStudent(Student student1,Student student2){
        return student1.getScore() - student2.getScore();
    }

然后我们代码可以这样写,这里用的是student1,其实用这四个中的任何一个都是可以的:

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",60);
        Student student2 = new Student("lisi",70);
        Student student3 = new Student("wangwu",80);
        Student student4 = new Student("zhaoliu",90);
        List<Student> students = Arrays.asList(student1,student2,student3,student4);
        students.sort((o1, o2) -> o1.getScore() - o2.getScore());
        students.forEach(student -> System.out.println(student.getScore()));
        students.sort(Student::compareStudentByScore);
        students.forEach(student -> System.out.println(student.getScore()));
        students.sort(student1::compareStudent);
        students.forEach(student -> System.out.println(student.getScore()));
    }

4、【类名::实例方法名】我们从上面看,其实这个实例方法里面调用的时候,实例是知道自己的分数的,那是不是可以不用在定义方法的时候,只定义一个入参,而不是两个,也就是:

    public int compareByScore(Student student){
        return this.getScore() - student.getScore();
    }

那要实现排序的需求,代码应该怎么写呢?

public static void main(String[] args) {
        Student student1 = new Student("zhangsan",60);
        Student student2 = new Student("lisi",70);
        Student student3 = new Student("wangwu",80);
        Student student4 = new Student("zhaoliu",90);
        List<Student> students = Arrays.asList(student1,student2,student3,student4);
        students.sort((o1, o2) -> o1.getScore() - o2.getScore());
        students.forEach(student -> System.out.println(student.getScore()));
        students.sort(Student::compareStudentByScore);
        students.forEach(student -> System.out.println(student.getScore()));
        students.sort(student1::compareStudent);
        students.forEach(student -> System.out.println(student.getScore()));
        students.sort(Student::compareByScore);
        students.forEach(student -> System.out.println(student.getScore()));
    }

现在我就有点困惑了,sort方法接收的lambda表达式不应该是两个参数么,为什么和int Comparator<Student>.compare(Student, Student)能匹配呢?因为实例方法有一个隐含的this参数,在方法实际调用的时候,第一个隐含参数总是传入this,相当于静态方法:public static int compareTo(this, Student o);

5、【类名::new】我们来讲最后一个类型的用法,看下面这个问题,如果要把一个List<String>转换为List<Student >,应该怎么办?

    public Student(String name){
        this.name = name;
        this.score = 0;
    }

传统的做法是先定义一个ArrayList<Student>,然后用for循环填充这个List

    public static void main(String[] args) {
        List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu");
        List<Student> studentList =  new ArrayList<>();
        for (String name : names) {
            studentList.add(new Student(name));
        }
    }

要更简单地实现String到Student的转换,我们可以引用Student的构造方法:

    public static void main(String[] args) {
        List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu");
        List<Student> studentList =  new ArrayList<>();
        for (String name : names) {
            studentList.add(new Student(name));
        }
        System.out.println(studentList.size());
        List<Student> studentList1 = names.stream().map(Student::new).collect(Collectors.toList());
        System.out.println(studentList1.size());
    }

是不是有点奇怪,现在我们来看一下map()方法的入参FunctionalInterface的定义:

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

把泛型进行替换以后就是方法签名Student apply(String),即传入参数String,返回类型Student。而Student类的构造方法恰好满足这个条件,因为构造方法的参数是String,虽然构造方法虽然没有return语句,但它会隐式地返回this实例,类型就是Student,因此,此处可以引用构造方法。

六、总结:

1、方法引用是为了简化lambda表达式而出现的

2、类名::实例方法名 比较特殊,需要注意一下,会隐式的传入对象本身

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值