初识方法引用
方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。在一些情况下, 比起使用Lambda表达式,它们似乎更易读,感觉也更自然。下面就是我们借助更新的Java 8 API (我们会在3.7节中更详细地讨论),用方法引用写的一个排序的例子:
//先前:
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
//之后(使用方法引用和java.util.Comparator.comparing):
inventory.sort(comparing(Apple::getWeight));
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。事实上,方法引用就是让你根据已有的方法实现来创建Lambda表达式。但是,显式地指明方法的名称,你的代码的可读性会更好。它是如何工作的呢?
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面。例如, Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法。
如何构建方法引用
方法引用主要有三类。
- (1) 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)。
- (2) 指 向 任 意 类 型 实 例 方 法 的 方 法 引 用 ( 例 如 String 的 length 方 法 , 写 作 String::length)。
- (3) 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction 用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)。
第二种和第三种方法引用可能乍看起来有点儿晕。类似于String::length的第二种方法引用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数。例如, Lambda表达式(String s) -> s.toUppeCase() 可以写作String::toUpperCase。但第三种方法引用指的是,你在Lambda中调用一个已经存在的外部对象中的方法。例如,Lambda表达式 ()->expensiveTransaction.getValue()可以写作expensiveTransaction::getValue。
构造函数引用
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用: ClassName::new。它的功能与指向静态方法的引用类似。例如,假设有一个构造函数没有参数。 它适合Supplier的签名() -> Apple。你可以这样做:
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
这就等价于:
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
如果你的构造函数的签名是Apple(Integer weight),那么它就适合Function接口的签名,于是你可以这样写:
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
这就等价于:
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);
如果你有一个具有两个参数的构造函数Apple(String color, Integer weight),那么它就适合BiFunction接口的签名,于是你可以这样写:
BiFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply("green", 110);
这就等价于:
BiFunction<String, Integer, Apple> c3 = (color, weight) -> new Apple(color, weight);
Apple c3 = c3.apply("green", 110);