1.函数方法

如果接口只有一个方法,则可以为该接口类型的变量分配一个由lambda 表达式(lambda 函数)给出的值。这样的接口被称为功能接口(在 Java 添加对 lambda 函数的支持之后)。

例如,Java 有Consumer<Type>接口,接口有accept(Type obj)方法。为什么需要这个接口?

在 Java 8 中,集合有一个forEach()方法,可以让你对集合中的每个元素执行一些操作 。这里的功能接口用于将操作传递给方法。 Consumer<T>forEach()

以下是显示集合的所有元素的方法:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach( (s) -> System.out.println(s) );
显示集合的所有元素(使用 lambda 表达式)

编译器会将上面的代码转换为下面的代码:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s));
   }
});
显示集合的所有元素(使用匿名类)

第一个版本肯定比第二个版本短。虽然带有 lambda 表达式的代码难以阅读,但带有匿名内部类的代码有时更难阅读。



2、方法参考

然而,我们的lambda 表达式代码可以写得更短。

首先,您可以省略参数周围的括号s

list.forEach( (s) -> System.out.println(s) );
list.forEach( s -> System.out.println(s) );

这只有在只有一个参数的情况下才能完成。如果有多个参数,则必须使用括号

其次,你可以这样写:

list.forEach( System.out::println );
最紧凑的符号

这是完全相同的符号。请注意,在 . 之后没有括号println

这里我们有相同的代码——一个方法调用:

object::method
x -> object.method(x)

想一想:我们想对list集合中的每个元素执行一些操作。如果操作是单个函数调用(例如println()),那么将函数作为参数简单地传递给方法是有意义的。

但是我们如何向编译器解释我们要传递方法而不是调用它呢?为此,我们在方法名称前使用了两个冒号,而不是点运算符。单个冒号已用于指示三元运算符。

这是最简单和最紧凑的表示法。



3.构造函数

当我们使用 I/O 流时,使用双冒号的方法引用非常方便。稍后你会看到这个。

同时,让我们谈谈传递方法引用的 3 种流行方式:

引用对象的方法

要传递对对象方法的引用,您需要编写类似. 此代码等效于.object::method
x -> object.method(x)

特殊thissuper变量可以用作对象。

引用类的方法

要传递对静态方法的引用,您需要编写类似. 此代码被转换为类似的代码class::methodx -> class.method(x);

引用构造函数

构造函数的行为类似于静态类方法,因此您也可以将引用传递给构造函数。这是它的样子:class::new

例如,您可以绕过集合的类型擦除并将方法传递toArray()给将创建所需数组的构造函数的引用:toArray(int[]::new);