3. 方法引用与构造器引用
3.1 方法引用
当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用!(可以理解为方法引用是Lambda 表达式的另外一种表现形式)
方法引用:使用操作符 ”::
“ 将方法名和对象或类的名字分隔开来。
有以下三种主要使用情况:
- 对象 :: 实例方法
- 类 :: 静态方法
- 类 :: 实例方法
example(1):
(x) -> System.out.println(x);
等同于:
System.out::println;
打印的这个操作 println()
这个方法已经实现了,所以就可以使用 方法引用。
example(2):
BinaryOperator<Double> bo = (x, y) -> Math.pow(x, y);
等同于:
BinaryOperator<Double> bo = Math::pow;
下面详细介绍一下使用方法引用的几种方式:
- 对象 :: 实例方法
example:
PrintStream ps1 = System.out;
Consumer<String> con = (x) -> ps1.println(x);
// 演化过程
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
Consumer<String> con2 = System.out::println;
con2.accept("abcdef");
Employee emp = new Employee(10, "liu", 18, 555.00); // Employee 是我自定义的类
Supplier<String> sup = () -> emp.getName(); // 使用 Lambda 表达式 体中获取 emp 的名字
String str = sup.get();
System.out.println(str);
Supplier<Integer> sup2 = emp::getAge; // 使用对象 :: 实例方法获取年龄
Integer num = sup2.get();
System.out.println(num);
- 类 :: 静态方法
example:
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com2 = Integer::compare;
注意①: 类 :: 静态方法 的引用有个**前提**,Lambda 体中的这个方法的参数列表和返回值类型要与我函数式接口中,抽象参数列表和返回值都相同
- 类 :: 实例方法
example:
BiPredicate<String, String> bp = (x, y) -> x.equals(y); // Lambda 表达式
BiPredicate<String, String> bp2 = String::equals; // 方法引用
注意②: 当参数列表中的第一个参数,是实例方法的调用者,参数列表第二个参数是 实例方法的参数实例(或无参数时)这种情况下就可以使用 类::实例方法名
再强调一下要注意的点:
注意:
* ① Lambda 体中调用的方法的参数列表与返回值类型,要与函数式接口中的抽象方法的参数列表和返回值保持一致!
* ② 若 Lambda 参数列表中的第一个参数,是实例方法的调用者,参数列表第二个参数是 实例方法的参数(或无参数)时,可以使用ClassName::method
3.2 构造器引用
格式:ClassName :: new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数
列表要与接口中抽象方法的参数列表一致!
example:
Supplier<Employee> sup = () -> new Employee();
// 原来 Lambda 表达式
Employee employee = sup.get();
// 构造器引用方式 默认使用无参构造器(用函数式接口的参数列表匹配构造器参数)
Supplier<Employee> sup2 = Employee::new;
可能有同学要问,它怎么知道我要用那构造器?实际上他是**用函数式接口的参数列表匹配构造器参数**。看下面这个例子
Function<Integer, Employee> fun = (x) -> new Employee(x);
Function<Integer, Employee> fun2 = Employee::new;
Employee employee = fun2.apply(101);
System.out.println(employee);
Function函数式接口是这样定义的
@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);
}
上面使用 Function 函数式接口,抽象方法里面有一个参数 T
那么就会去找 Employee 里面找对应的构造器,前提是 Employee 类里有对应的构造器,如果没有语法上会报错的。
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!
3.3 数组引用
格式:Type[] :: new
example:
Function<Integer, String[]> fun = (x) -> new String[x]; // Lambda 表达式
String[] apply = fun.apply(10);
System.out.println(apply.length);
Function<Integer, Integer[]> fun2 = Integer[]::new; // 数组引用
Integer[] apply1 = fun2.apply(20);
System.out.println(apply1.length);
总结:不管是方法引用和构造器引用只不过是基于原来的语法进行的改进,变种成一种更简洁的语法,看起来更美观,也更方便,更利于使用。