一、什么是方法引用
在Lambda表达式中,将其划分了几块。这一行就是lambda表达式。() -> System.out.println("使用Lambda表达式");下面我们对lambda的格式进行一个介绍:
(1)左边括号:lambda的形参列表,就好比是我们定义一个接口,里面有一个抽象方法,这个抽象方法的形参列表。
(2)箭头:lambda的操作符,所以你看见这个箭头心中知道这是一个lambda表达式就可以了。
(3)右边lambda体:就好比是我们实现了接口中的抽象方法。
在最后一块中,有时候,lambda体可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。
所以我们来总结:方法引用是一个更加紧凑,易读的 Lambda 表达式,注意方法引用是一个 Lambda 表达式,其中方法引用的操作符是双冒号 "::"。
二、举例演示
1、我们首先定义一个User类
public class Person { String name; LocalDate birthday; public Person(String name, LocalDate birthday) { this.name = name; this.birthday = birthday; } public LocalDate getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } @Override public String toString() { return this.name; } }
现在有一个功能,那就是需要按照年龄,对Person进行排序。
2、Lambda表达式写法
public class RefSort { public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("1", LocalDate.of(2019, 12, 1)), new Person("2", LocalDate.of(2019, 12, 2)), new Person("3", LocalDate.of(2019, 12, 3)), new Person("4", LocalDate.of(2019, 12, 4))}; Arrays.sort(pArr, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); }); System.out.println(Arrays.asList(pArr)); } }
我们使用lambda表达式可以直接进行比较。现在我们使用方法引用。
public class RefSort { public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("1", LocalDate.of(2019, 12, 1)), new Person("2", LocalDate.of(2019, 12, 2)), new Person("3", LocalDate.of(2019, 12, 3)), new Person("4", LocalDate.of(2019, 12, 4))}; Arrays.sort(pArr, Person::compareByAge); System.out.println(Arrays.asList(pArr)); } }
这下子代码是不是更加的简洁了。我们相当于把lambda表达式进行了改变。下面我们就来好好地讲解一下方法引用是如何使用的。
三、方法引用分类
先来说一下方法引用使用到的操作符“::”,这个操作符把方法引用分成两边,左边是类名或者某个对象的引用,右边是方法名。引用方法有下面几种方式:
(1)对象引用::实例方法名
(2)类名::静态方法名
(3)类名::实例方法名
(4)类名::new
(5)类型[]::new
我们对这5种方式分别来叙述。
1、对象引用::实例方法名
public class PersonCompare { public int compareByName(Person a, Person b) { return a.getName().compareTo(b.getName()); } public int compareByAge(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } @Test public void test(){ Person[] pArr = new Person[]{ new Person("1", LocalDate.of(2019, 12, 1)), new Person("2", LocalDate.of(2019, 12, 2)), new Person("3", LocalDate.of(2019, 12, 3)), new Person("4", LocalDate.of(2019, 12, 4))}; PersonCompare personCompare = new PersonCompare(); Arrays.sort(pArr, personCompare::compareByName); } }
我们创建了一个PersonCompare对象,调用了其内部的compareByName实例方法。
2、类名::静态方法名
举几个例子:
String::valueOf,等价于 Lambda:s -> String.valueOf(s)
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y);
Person::compareByAge 就是一个静态方法引用,也是前面我们举得例子。
Function<Long, Long> f = Math::abs; Long result = f.apply(-3L);123
Math是一个类而abs为该类的静态方法。Function中的唯一抽象方法apply方法参数列表与abs方法的参数列表相同,都是接收一个Long类型参数。
3、类名::实例方法名
若Lambda表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法:
BiPredicate<String, String> b = String::equals; b.test("abc", "abcd");
String是一个类而equals为该类的定义的实例方法。BiPredicate中的唯一抽象方法test方法参数列表与equals方法的参数列表相同,都是接收两个String类型参数。
4、类名::new
在引用构造器的时候,构造器参数列表要与接口中抽象方法的参数列表一致。对应的 Lambda:() -> new String()。
class PersonFactory { private Supplier<Person> supplier; public PersonFactory(Supplier<Person> supplier) { this.supplier = supplier; } public Person getPerson() { return supplier.get(); } } PersonFactory factory = new PersonFactory(Person::new); Person p1 = factory.getPerson();
上面这段代码的功能就是使用PersonFactory工厂,然后使用Person::new创建一个Person实例。
5、类型[]::new
引用数组和引用构造器很像,格式为 类型[]::new,等价于 lambda 表达式 x -> new int[x]。其中类型可以为基本类型也可以是类。
Function<Integer, int[]> fun = int[]::new; int[] arr = fun.apply(10); Function<Integer, Integer[]> fun2 = Integer[]::new; Integer[] arr2 = fun2.apply(10);
OK。方法引用就是这么简单。