一、什么是方法引用?
当我们在写代码的时候,发现方法的入参是一个函数接口,比如我们在对数组进行排序,需要使用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、类名::实例方法名 比较特殊,需要注意一下,会隐式的传入对象本身