JDK8中引入了一种新的特性—方法引用(Method Reference),方法引用是Lambda的一种特例化形式,当Lambda表达式的主体部分只有一句,并且有一个特定的方法已经存在,那么可以使用方法引用来代替展示。
方法引用实际上是Lambda表达式的一种语法糖,所谓语法糖,就是并没有增加新的内容,只不过用一种更加简洁的形式来表现。
方法引用的分类:
- 类名::静态方法名
- 引用名(对象名)::实例方法名
- 类名::实例方法名
- 类名::new
方法引用最显著的特点是双冒号,对于第三种(类名::实例方法名)来说,Lambda表达式的第一个参数是实例方法的调用者,其他的参数作为实例方法的参数;对于第四种(类名::new),编译器会自动地进行推断。
代码示例:
准备阶段
@Data
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class StudentCompare {
//根据名字进行排序(静态方法)
public static int compareStudentByName(Student student1, Student student2) {
return student1.getName().compareToIgnoreCase(student2.getName());
}
//根据年龄进行排序(静态方法)
public static int compareStudentByAge(Student student1, Student student2) {
return student1.getAge() - student2.getAge();
}
//根据名字进行排序(非静态方法)
public int compareStudentByNameNotStatic(Student student1, Student student2) {
return student1.getName().compareToIgnoreCase(student2.getName());
}
//根据年龄进行排序(非静态方法)
public int compareStudentByAgeNotStatic(Student student1, Student student2) {
return student1.getAge() - student2.getAge();
}
}
准备阶段有一个Student类,参数很简单,只有姓名和年龄,StudentCompare类的作用是提供静态方法和非静态方法来帮助按照年龄或者姓名来排序。
使用Lambda表达式的方式排序并输出排序后的结果
public class MethodReferenceDemo {
public static void main(String[] args) {
//数据准备
Student student1 = new Student("aaa", 25);
Student student2 = new Student("ccc", 27);
Student student3 = new Student("bbb", 26);
Student student4 = new Student("ddd", 28);
List<Student> students = Arrays.asList(student1, student2, student3, student4);
//Lambda表达式的方式排序并输出排序后的结果(静态方法)
students.sort((stu1, stu2) -> StudentCompare.compareStudentByName(stu1, stu2));
students.forEach(stu -> System.out.println("name:" + stu.getName()));
System.out.println("------------");
//Lambda表达式的方式排序并输出排序后的结果(非静态方法)
StudentCompare studentCompare = new StudentCompare();
students.sort((stu1, stu2) -> studentCompare.compareStudentByAge(stu1, stu2));
students.forEach(stu -> System.out.println("age:" + stu.getAge()));
}
}
输出结果:
使用方法引用的方式排序并输出排序后的结果
public class MethodReferenceDemo {
public static void main(String[] args) {
//数据准备
Student student1 = new Student("aaa", 25);
Student student2 = new Student("ccc", 27);
Student student3 = new Student("bbb", 26);
Student student4 = new Student("ddd", 28);
List<Student> students = Arrays.asList(student1, student2, student3, student4);
//方法引用的方式排序并输出排序后的结果(类名::静态方法名)
students.sort(StudentCompare::compareStudentByName);
students.forEach(stu -> System.out.println("name:" + stu.getName()));
System.out.println("------------");
//方法引用的方式排序并输出排序后的结果( 引用名(对象名)::实例方法名)
StudentCompare studentCompare = new StudentCompare();
students.sort(studentCompare::compareStudentByAgeNotStatic);
students.forEach(stu -> System.out.println("age:" + stu.getAge()));
}
}
输出结果:
结果分析:可以看到,使用方法引用的方式排序
和使用Lambda表达式的方式排序
的结果是一样的,因为方法引用实际上就是Lambda表达式的一种语法糖
,以上演示的是方法引用中的前两种,还是很容易理解的,因为StudentCompare类中有特定的方法可以替代Labmda表达式的主体,所以才可以转换为方法引用。
类名::实例方法名形式的方法引用
public static void main(String[] args) {
List<String> list = Arrays.asList("chengdu ", "nanjing", "shanghai", "beijing");
list.sort(String::compareToIgnoreCase); //(1)类名::实例方法名
list.forEach(System.out::println); //(2)引用名::实例方法名
}
输出结果:
结果分析:正常来说,compareToIgnoreCase()
的用法应该是:System.out.println("beijing".compareToIgnoreCase("nanjing"));
,也就是某个字符串调用方法与另一个字符串作比较,查看List中的sort()方法可以看出,其参数是一个Comparator接口,而该接口是一个函数式接口,int compare(T o1, T o2);
是Comparator接口中的抽象方法,而上文中提到,类名::实例方法名
的方法引用方式等价于Lambda表达式的第一个参数是实例方法的调用者,其他的参数作为实例方法的参数
,所以(1)处的是类名::实例方法名这种形式的方法引用。System.out
等价于public final static PrintStream out = null;
而PrintStream是个对象,println()是该对象的一个方法,所以(2)处是引用名::实例方法名这种形式的方法引用。
List中的sort()方法源码
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
类名::new形式的方法引用
public class MethodReferenceDemo {
public String getString(Supplier<String> stringSupplier) {
return stringSupplier.get() + "world";
}
public String getString2(String string, Function<String, String> function) {
return function.apply(string);
}
public static void main(String[] args) {
MethodReferenceDemo methodReferenceDemo = new MethodReferenceDemo();
System.out.println(methodReferenceDemo.getString(() -> "")); //a
System.out.println(methodReferenceDemo.getString(String::new)); //b
System.out.println(methodReferenceDemo.getString2("hello", value -> value)); //c
System.out.println(methodReferenceDemo.getString2("hello", String::new)); //d
}
输出结果:
结果分析:注释a的代码等价于注释b的代码,注释c的代码等价于注释d的代码。可见类名::new形式的方法引用也是Lambda的一种语法糖形式。
学习方法应用可以帮助我们更好的理解Lambda表达式,也可以帮助我们更好的阅读他人的代码,学习并使用fangfa引用还是很有必要的。