在上一篇博客中,我们继续介绍了一些关于 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 表达式构建方法引用的办法
接下来我就用一个例子——给学生利用不同策略来排序为大家总结一下,行为参数化,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<T,R> 的(T) -> R 的函数签名,因此我们可以用以下几种不同的方法创建一个 Student 实例:
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<T,U,R> 的 <T,U> -> R 的函数签名,因此我们可以用以下几种不同的方法创建一个 Student 实例:
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());
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于