上一节介绍了Java8新特性中的Lambda表达式,本小节继续讲解Java8的新特性之二:方法引用。方法引用其实也离不开Lambda表达式。
1、方法引用的使用场景
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。方法引用可以理解为Lambda表达式的另外一种表现形式。
2、方法引用的分类
3、方法引用举例
3.1 静态方法引用
有一个Person类,如下所示:
1 @Data 2 public class Person { 3 4 private String name; 5 6 private Integer age; 7 8 public static int compareByAge(Person a, Person b) { 9 return a.age.compareTo(b.age); 10 } 11 }
现假设,一个部门有30人,把他们存放在一个数组中,并按年龄排序,通常我们可以自己写一个比较器,代码如下:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加数组元素省略 3 4 class PersonAgeComparator implements Comparator<Person> { 5 public int compare(Person a, Person b) { 6 return a.getBirthday().compareTo(b.getBirthday()); 7 } 8 } 9 10 Arrays.sort(rosterAsArray, new PersonAgeComparator());
Arrays.sort的声明为:public static <T> void sort(T[] a, Comparator<? super T> c),比较器参数Comparator为一个函数式接口,利用上一节Lambda表达式所学知识,可以改写为以下代码:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加数组元素省略 3 4 Arrays.sort(rosterAsArray, (a,b) -> a.getAge().compareTo(b.getAge()));
然而,你会发现,Perdon类中已经有了一个静态方法的比较器:compareByAge,因此,我们改用Person类已经提供的比较器:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加数组元素省略 3 4 Arrays.sort(rosterAsArray, (a,b) -> Person.compareByAge(a,b));
以上代码,因为Lambda表达式调用了一个已经存在的静态方法,根据我们第2节表格中的语法,上面的代码可以最终改写成静态方法引用:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加数组元素省略 3 4 Arrays.sort(rosterAsArray, Person::compareByAge);
下面这个例子更简单:
1 public class Test { 2 public static void main(String[] args) { 3 List<Integer> list = Arrays.asList(82,22,34,50,9); 4 list.sort(Integer::compare); 5 System.out.println(list); 6 } 7 }
对一个Integer列表进行排序,因为Integer中已经存在静态的比较方法compare(),因此可以直接用静态方法引用的方式来调用 ,运行结果为:
[9, 22, 34, 50, 82]
3.2 实例方法引用
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
1 @Data 2 class User { 3 4 private String name; 5 private Integer age; 6 7 public User(String name, Integer age) { 8 this.name = name; 9 this.age = age; 10 } 11 } 12 13 public class TestInstanceReference { 14 15 public static void main(String[] args) { 16 17 TestInstanceReference test = new TestInstanceReference(); 18 User user = new User("欧阳峰",32); 19 Supplier<String> supplier = () -> user.getName(); 20 System.out.println("Lambda表达式输出结果:" + supplier.get()); 21 22 Supplier<String> supplier2 = user::getName; 23 System.out.println("实例方法引用输出结果:" + supplier2.get()); 24 } 25 }
输出结果:
Lambda表达式输出结果:欧阳峰
实例方法引用输出结果:欧阳峰
3.3 对象方法引用
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
String的equals()方法:
1 public boolean equals(Object anObject) { 2 if (this == anObject) { 3 return true; 4 } 5 if (anObject instanceof String) { 6 String anotherString = (String)anObject; 7 int n = value.length; 8 if (n == anotherString.value.length) { 9 char v1[] = value; 10 char v2[] = anotherString.value; 11 int i = 0; 12 while (n-- != 0) { 13 if (v1[i] != v2[i]) 14 return false; 15 i++; 16 } 17 return true; 18 } 19 } 20 return false; 21 }
1 public static void main(String[] args) { 2 3 BiPredicate<String,String> bp = (x, y) -> x.equals(y); 4 BiPredicate<String,String> bp1 = String::equals; 5 6 boolean test = bp1.test("xy", "xx"); 7 System.out.println(test); 8 }
BiPredicate的test()方法接受两个参数,x和y,具体实现为x.equals(y),满足Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数,因此可以使用对象方法引用。
3.4 构造方法引用
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。
如:要获取一个空的User列表:
1 Supplier<List<User>> userSupplier = () -> new ArrayList<>(); 2 List<User> user = userSupplier.get(); 3 4 Supplier<List<User>> userSupplier2 = ArrayList<User>::new; // 构造方法引用写法 5 List<User> user2 = userSupplier.get();
至此,方法引用讲完了,下一章节将讲解Stream API。