Java8 Lambda表达式(二)System.out::println与Lambda表达式

目录

1 System.out::println在哪里出现

2 为什么会有System.out::println

2.1 List接口的forEach方法

2.2 Consumer接口

3 Lambda方法引用

4 总结


1 System.out::println在哪里出现

        在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<Integer> numList = new ArrayList<>();
        Collections.addAll(numList, 1, 2, 3, 4, 5, 6);
        // 遍历numList,并依次打印其中的元素
        numList.forEach(System.out::println);
    }
}

        在这段代码中就将System.out::println作为了numList.forEach的参数,执行的结果后是在控制台依次打印了numList中的六个元素。

2 为什么会有System.out::println

        Lambda表达式是一个匿名函数,换句话说,Lambda表达式表达了一个被实现的接口,这个接口中只有一个抽象方法,在实现这一个接口后创建对象并将这个对象作为参数传给了目标方法。那么,这里numList.forEach方法的参数一定是一个接口。

2.1 List接口的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方法。

2.2 Consumer接口

        我们找到Consumer,它是一个接口,并且是一个函数式接口,下面为这个接口的代码复制。

package java.util.function;

import java.util.Objects;


@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> 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<T>接口的匿名函数,实现了其中的void accept(T t);方法。

3 Lambda方法引用

        这里的System.out::println确实是实现Consumer<T>接口的匿名函数,而且这里用的是Lambda表达式中的方法引用语法。方法引用实际上是将一个Lambda表达式的实现指向一个已经实现了的方法,语法为——方法的隶属者::方法名。对于System.out::println来说,System.out就是方法的隶属者,println就是已实现的方法。我们依然找到它的源码。

public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

 

4 总结

        根据源码中的内容可以知道,上面例子中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<Integer> numList = new ArrayList<>();
        Collections.addAll(numList, 1, 2, 3, 4, 5, 6);
        System.out.println();
        // 遍历numList,并依次打印其中的元素
        numList.forEach((t) -> {
            System.out.println(t);
        });
    }
}

        

发布了48 篇原创文章 · 获赞 52 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/y506798278/article/details/104503588