其实,我们已经给编译器提供了足够多的信息,可以把一个方法引用转换成一个等同的 Lambda。当我们调用convert方法时,我们可以把如下代码传递给 Lambda。
convert(100, (number) -> String.valueOf(number));我们可以用把上面的 Lambda 替换为方法引用,
convert(100, String::valueOf);另一种方式是我们告诉编译器,把引用分配给一个类型:
Conversion b = (number) -> String.valueOf(number);用方法引用来表示:
Conversion b = String::valueOf 方法引用的种类在 Java 中,有四种方法引用的类型:
最后两个有点混乱。第一种是特定对象的方法引用,第二个是任意对象的方法引用,而是特定类型的方法引用。区别在于你想如何使用该方法,如果你事先并不知道有没有实例。
构造方法引用构造方法的基本引用如下:
String::new它会创建一个 Lambda 表达式,然后调用String 无参的构造方法。
它实际上等同于:
需要注意的是构造方法引用没有括号,它只是引用,并不是调用,上面的例子只是引用了 String类的构造方法,并没有真正去实例化一个字符串对象。
接下来我们看一个实际应用构造方法引用的例子。
看先的例子,循环十遍为 list 增加对象。
如果我们想复用实例化的功能,我们可以抽取出一个新的方法initialise用factory创建对象。
public void usage() { List<Object> list = new ArrayList<>(); initialise(list, ...); } private void initialise(List<Object> list, Factory<Object> factory){ for (int i = 0; i < 10; i++) { list.add(factory.create()); } }Factory是一个函数式接口,包含一个create方法,此方法返回 Object 对象,我们可以用 Lambda 的方式向 list 中添加对象。
public void usage() { List<Object> list = new ArrayList<>(); initialise(list, () -> new Object()); }或者我们用构造方法引用的方式来替换:
public void usage() { List<Object> list = new ArrayList<>(); initialise(list, Object::new); }上面的方法其实还有待改进,上面只是创建 Object 类型的对象,我们可以增加泛型,实现可以创建更多类型的方法。
public void usage() { List<String> list = new ArrayList<>(); initialise(list, String::new); } private <T> void initialise(List<T> list, Factory<T> factory) { for (int i = 0; i < 10; i++) { list.add(factory.create()); } }到现在为知,我们演示的都是无参的构造方法的引用,如果是带有参数的构造方法的引用该如何处理呢?
当有多个构造函数时,使用相同的语法,但编译器计算出哪个构造函数是最佳匹配。它基于目标类型和推断功能接口,它可以用来创建该类型。
例如,我们有个 Person 类,它有一个多个参数的构造方法。
回到上面的例子,我们可以如下使用:
initialise(people, () -> new Person(forename, surname, birthday, gender, email, age));但是如果想使用这个构造方法引用,则需要 Lambda 表达式提供如下参数:
initialise(people, () -> new Person(forename, surname, birthday, gender, email, age)); 特定对象的方法引用下面是特定对象的方法引用的例子:
x::toStringx就是我们想要得到的对象。它等同于下面的Lambda 表达式。
() -> x.toString()这种方法引用可以为我们提供便利的方式在不同的函数式接口类型中进行切换。看例子:
Callable<String> c = () -> "Hello";Callable的方法为call,当被调用时返回“Hello”。
如果我们有另外一个函数式接口Factory,我们可以使用方法引用的方式来转变Callable这个函数式接口。
我们可以重新创建一个 Lambda表达式,但是这个技巧是重用预定义的Lambda的一个有用的方式。 将它们分配给变量并重用它们以避免重复。
我们有下面一个例子:
这个例子中方法引用使用了闭包。他创建了一个 Lambda用来调用x对象上的toString方法。
上面function方法的签名和实现如下所示:
函数式接口Supplier的定义如下:
@FunctionalInterface public interface Supplier<T> { T get(); }当使用此方法时,它通过get方法返回一个字符串,而且这是唯一的在我们的结构中获取字符串的方式。它等同于:
public void example() { String x = ""; function(() -> x.toString()); }需要注意的是,这里的 Lambda 表达式没有参数。这表明x变量在Lambda的局部作用域里是不可用的,如果可用必须要放在它的作用域之外。我们必须要掩盖变量x。
如果用匿名类来实现的话,应该是下面的样子,这些需要主意,x变量是如何传递的。
最后一种类型的实例方法引用的格式是这样的:
Object::toString