参考文章: http://zh.lucida.me/blog/java-8-lambdas-insideout-language-features/
Java8 的方法引用 是基于 lambda 表达式的,可以认为是对 lambda 表达式的一种扩展,如果对 lambda 表达式不太了解,可以参考我的文章。 http://blog.csdn.net/u010003835/article/details/76833247
1. 方法引用(Method references)
lambda 表达式允许我们定义一个匿名方法,并允许我们以函数式接口的方式使用它。我们也希望能够在 已有的 方法上实现同样的特性。
方法引用和 lambda 表达式拥有相同的特性(例如,它们都需要一个目标类型,并需要被转化为函数式接口的实例),不过我们并不需要为方法引用提供方法体,我们可以直接通过方法名称引用已有方法。
以下面的代码为例,假设我们要按照 name
或 age
为 Person
数组进行排序:
|
|
在这里我们可以用方法引用代替lambda表达式:
|
|
这里的 Person::getName
可以被看作为 lambda 表达式的简写形式。尽管方法引用不一定(比如在这个例子里)会把语法变的更紧凑,但它拥有更明确的语义——如果我们想要调用的方法拥有一个名字,我们就可以通过它的名字直接调用它。
因为函数式接口的方法参数对应于隐式方法调用时的参数,所以被引用方法签名可以通过放宽类型,装箱以及组织到参数数组中的方式对其参数进行操作,就像在调用实际方法一样:
|
|
2. 方法引用的种类(Kinds of method references)
方法引用有很多种,它们的语法如下:
- 静态方法引用:
ClassName::methodName
- 实例上的实例方法引用:
instanceReference::methodName
- 超类上的实例方法引用:
super::methodName
- 类型上的实例方法引用:
ClassName::methodName
- 构造方法引用:
Class::new
- 数组构造方法引用:
TypeName[]::new
对于静态方法引用,我们需要在类名和方法名之间加入 ::
分隔符,例如 Integer::sum
对于具体对象上的实例方法引用,我们则需要在对象名和方法名之间加入分隔符:
|
|
这里的隐式 lambda 表达式(也就是实例方法引用)会从 knownNames
中捕获 String
对象,而它的方法体则会通过Set.contains
使用该 String
对象。
有了实例方法引用,在不同函数式接口之间进行类型转换就变的很方便:
|
|
引用任意对象的实例方法则需要在实例方法名称和其所属类型名称间加上分隔符:
|
|
这里的隐式 lambda 表达式(即 String::toUpperCase
实例方法引用)有一个 String
参数,这个参数会被 toUpperCase
方法使用。
如果类型的实例方法是泛型的,那么我们就需要在 ::
分隔符前提供类型参数,或者(多数情况下)利用目标类型推导出其类型。
需要注意的是,静态方法引用和类型上的实例方法引用拥有一样的语法。编译器会根据实际情况做出决定。
一般我们不需要指定方法引用中的参数类型,因为编译器往往可以推导出结果,但如果需要我们也可以显式在 ::
分隔符之前提供参数类型信息。
和静态方法引用类似,构造方法也可以通过 new
关键字被直接引用:
|
|
如果类型拥有多个构造方法,那么我们就会通过目标类型的方法参数来选择最佳匹配,这里的选择过程和调用构造方法时的选择过程是一样的。
如果待实例化的类型是泛型的,那么我们可以在类型名称之后提供类型参数,否则编译器则会依照”菱形”构造方法调用时的方式进行推导。
数组的构造方法引用的语法则比较特殊,为了便于理解,你可以假想存在一个接收 int
参数的数组构造方法。参考下面的代码:
|
|
3. 综合的例子
package lambda;
class AlreadyExist {
public static void MM(Integer a) {
System.out.println("通过类的静态函数引用 数据是 :" + a);
}
public void KK(Integer b) {
System.out.println("通过类的非静态函数引用 数据是 :" + b * b);
}
}
@FunctionalInterface
interface Cmd {
void DoSomething(Integer data);
}
interface DefaultCmd {
default void DefaultMethod(Integer data) {
System.out.println("接口的默认函数 输出是: " + data);
}
}
/**
* Created by szh on 2017/8/7.
*/
public class FuncQuote {
public static void main(String[] args) {
Cmd a = (n) -> System.out.println("通过lambda表达式 数据是 :" + n);
a.DoSomething(5);
Cmd b = AlreadyExist::MM;
b.DoSomething(5);
AlreadyExist s = new AlreadyExist();
Cmd c = s::KK;
c.DoSomething(5);
DefaultCmd defaultCmd = new DefaultCmd() {
};
Cmd d = defaultCmd::DefaultMethod;
d.DoSomething(5);
}
}