方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。
在一些情况下,比起使用Lambda表达式,它们似乎更易读,感觉也更自然。
下面就是借助更新的Java8 API用方法引用写的一个排序的例子:
6X28A`M~%WN]DI{(Q${F@C7.png
为什么应该关心方法引用?
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。
它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。
事实上,方法引用就是让你根据已有的方法实现来创建Lambda表达式。
但是显式地指明方法的名称,你的代码的可读性会更好。
它是如何工作的呢?
当你需要使用方法引用时,目标引用放在分隔符 : : 前,方法的名称放在后面。
例如,Apple: :getWeight就是引用了Apple类中定义的方法getweight。
请记住,不需要括号,因为你没有实际调用这个方法。
方法引用就是Lambda表达式(Apple a) -> a. getweight ()的快捷写法。
~1%}R@F7UI7P((_T4`C)ORV.png
你可以把方法引用看作针对仅仅涉及单一方法的Lambda的语法糖,因为你表达同样的事情时要写的代码更少了。

如何构建方法引用

方法引用主要有三类。
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。
依照一些简单的方子,就可以将Lambda表达式重构为等价的方法引用,如图3-5所示。
@GU5RXEX@T4BGFEW09@WZ_B.png
请注意,还有针对构造函数、数组构造函数和父类调用( super-call)的一些特殊形式的方法引用。
举一个方法引用的具体例子吧。
比方说你想要对一个字符串的List排序,忽略大小写。
List的sort方法需要一个Comparator作为参数,Comparator描述了一个具有(T, T) -> int签名的函数描述符。
你可以利用String类中的compareToIgnoreCase方法来定义一个Lambda表达式(注意compareToIgnoreCase是String类中预先定义的)。
@X5VVGCMJ35P62{)K@K94Q5.png
Lambda表达式的签名与Comparator的函数描述符兼容。
利用前面所述的方法,这个例子可以用方法引用改写成下面的样子:
X4S_E}BUCWQKDTRX@SCNKZU.png
请注意,编译器会进行一种与Lambda表达式类似的类型检查过程,来确定对于给定的函数式接口,这个方法引用是否有效:方法引用的签名必须和上下文类型匹配。
K(_I[8C1S0N40{YO6WQ]`UY.png

构造函数引用

对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:ClassName :: new。
它的功能与指向静态方法的引用类似。
例如,假设有一个构造函数没有参数。
它适合supplier的签名() -> Apple。
可以这样做:
VX%QH}C)37[)E_X%9`][N7Q.png](https://cdn.nlark.com/yuque/0/2021/png/12773302/1633578536828-b09782e3-0854-447d-95b8-c251eeaf8e77.png#clientId=ua0843ab3-7354-4&from=drop&id=lXqc0&margin=%5Bobject%20Object%5D&name=VX%25QH%7DC%2937%5B%29E_X%259%60%5D%5BN7Q.png&originHeight=148&originWidth=832&originalType=binary&ratio=1&size=30236&status=done&style=none&taskId=uf09ee91b-e8b5-43e1-8091-6b9a0f4f1c9)<br />等价于:<br />![TS}J(0$01B~7J8_@[E6B[_M.png](https://cdn.nlark.com/yuque/0/2021/png/12773302/1633578596120-6d357795-fefa-41df-bd42-5c29acfda154.png#clientId=ua0843ab3-7354-4&from=drop&id=uf817020c&margin=%5Bobject%20Object%5D&name=TS%7DJ%280%2401B~7J8_%40%5BE6B%5B_M.png&originHeight=139&originWidth=836&originalType=binary&ratio=1&size=30857&status=done&style=none&taskId=u9461359c-cb39-4e9b-9a2e-fd1d490353e)<br />如果构造函数的签名是Apple (Integer weight), 那么它就适合Function接口的签名,于是你可以这样写:<br />![X~2YHT23}]$XXYE8F7OUIA7.png
等价于:
MG)HB$JXV)I}EZ4ZN$AY0B9.png
在下面的代码中,一个由Integer构成的List中的每个元素都通过我们前面定义的类似的map方法传递给了Apple的构造函数,得到了一个具有不同重量苹果的List:
![%64W1`942{UHQ7XECPJV93.png
如果你有一个具有两个参数的构造函数Apple(String color, Integer weight), 那么它就适合BiFunction接口的签名,于是你可以这样写:
I@PC9K`OS8N)LEZ(JDF{S2N.png
等价于:
![Y0G7W7PVFYDX$39LOM0[02.png](https://cdn.nlark.com/yuque/0/2021/png/12773302/1633581203384-cd7d5f6f-5546-4fb2-bc77-f26b80f5a3f9.png#clientId=ua0843ab3-7354-4&from=drop&id=u0b7e04b6&margin=%5Bobject%20Object%5D&name=Y0G7W7PVF%60YDX%2439LOM0%5B02.png&originHeight=132&originWidth=689&originalType=binary&ratio=1&size=39842&status=done&style=none&taskId=uab508216-122b-49b8-8b10-e8e5fe71470)<br />![R)~4QDAENSN(XSMYSQ`~}9.png](https://cdn.nlark.com/yuque/0/2021/png/12773302/1633581229871-1e5433e7-99e2-46cd-b818-4b621af60c8f.png#clientId=ua0843ab3-7354-4&from=drop&id=ueb66f932&margin=%5Bobject%20Object%5D&name=R%29~4QDAENSN%28XSM%60YSQ%60~%7D9.png&originHeight=297&originWidth=827&originalType=binary&ratio=1&size=112962&status=done&style=none&taskId=u9fca617b-48b1-46f1-9cbc-d1bd722996d)