目录
1 System.out::println在哪里出现
2 为什么会有System.out::println
2.1 List接口的forEach方法
2.2 Consumer接口
3 Lambda方法引用
4 总结
在Java中,执行System.out.println("hello world");会在控制台打印“hello world”;而在Java8引入了Lambda表达式这一新特性后,是可能出现System.out::println这样的代码的,比如下面这段代码。
public class TestPrint {
public static void main(String[] args) {
// 创建一个Integer类型的List集合,并添加6个元素
List numList = new ArrayList<>();
Collections.addAll(numList, 1, 2, 3, 4, 5, 6);
// 遍历numList,并依次打印其中的元素
numList.forEach(System.out::println);
}
}
在这段代码中就将System.out::println作为了numList.forEach的参数,执行的结果后是在控制台依次打印了numList中的六个元素。
Lambda表达式是一个匿名函数,换句话说,Lambda表达式表达了一个被实现的接口,这个接口中只有一个抽象方法,在实现这一个接口后创建对象并将这个对象作为参数传给了目标方法。那么,这里numList.forEach方法的参数一定是一个接口。
forEach是List接口所提供的方法,它有一个默认实现,我们在IDEA中找到JDK源码中的这一段代码,如下所示。
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
这段代码的语义是遍历this,也就是当前的List对象,这里是numList。在遍历这一个集合时回去调用作为参数的action的accept方法。
我们找到Consumer,它是一个接口,并且是一个函数式接口,下面为这个接口的代码复制。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer {
void accept(T t);
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
andThen是一个默认实现的方法,这里我们不管,那么就只剩下一个void accept(T t)方法,上面的System.out::println就是Consumer
这里的System.out::println确实是实现Consumer
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
根据源码中的内容可以知道,上面例子中Lambda表达式关联对象的关系如下:
1.List接口中forEach方法的参数类型为Consumer接口;
2.Consumer是一个函数式接口,除了一个defalut方法外只有一个名为accept的抽象方法;
3.System.out::println这一个Lambda表达式实现了Consumer中的accept方法,用的是引用的方式引用了System.out中的println方法。
所以,最上面一段遍历集合并打印的代码也可以写成如下形式,运行的结果是一样的。
public class TestPrint {
public static void main(String[] args) {
// 创建一个Integer类型的List集合,并添加6个元素
List numList = new ArrayList<>();
Collections.addAll(numList, 1, 2, 3, 4, 5, 6);
System.out.println();
// 遍历numList,并依次打印其中的元素
numList.forEach((t) -> {
System.out.println(t);
});
}
}