System.out::println

最近几天在学依赖注入框架Dagger。可能是因为总是查阅kotlin把java给忘了,读实例代码的时候有时候反应不过来(比如System.out::println的真正含义),不由得写点东西复习一下。。。

Dagger示例代码

  • 网页链接
  • 代码片段
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    interface Outputter {
        void output(String output);
    }
    
    ...
    @Module
    abstract class SystemOutModule {
        @Provides
        static Outputter textOutputter() {
            return System.out::println;
        }
    }
    

    上述例子是为了说明@provides注解是用来向依赖注入请求提供实例的。方法textOutputter显然返回Outputter的实例,该实例(实现)通过System.out::println得到。

Java方法引用之实例方法引用

  • 语法

    1
    2
    
    containingObject::instanceMethodName
    //实例:方法
    
  • System.out.println是谁
    常用的System.out.prinln中,System是包java.lang下的final类。out则是该类中的标准输出流的静态变量。

    1
    
    public static final PrintStream out = null;
    

    通过System.out可以获得一个PrintStream实例。println正是该PrintStream类中的方法,它接受一个String参数,返回void:

    1
    
      public void println(String x) {...}
    

    那么作为返回类型的Outputter是什么呢?是一个接口,有且只有一个抽象方法,因此是一个函数式接口

  • 从匿名函数到lambda再到方法引用
    将接口的实现赋值给接口类型的变量,在java8之前,我们可以用匿名函数来实现:

    1
    2
    3
    4
    5
    6
    
    Outputter outputter = new Outputter() {
        @Override
        public void output(String input) {
            System.out.println(input);
        }
    };
    

    java8推出lambda表达式以后,就可以将代码简化成lambda表达式:

    1
    
    Outputter outputter = input -> System.out.println(input);
    

    箭头前是参数,箭头后是对参数的执行代码块。

    将上述表达式再简化一点,就可以写成方法引用的形式:

    1
    
    Outputter outputter = System.out::println;
    

    以上几种写法本质上都是相同的,都是对函数式接口进行了匿名实现,将其赋值给接口类型的变量。

    现在再回到开头的Dagger示例就可以很快反应过来,return中的方法引用表达式,确实可以获得Outputter类型的返回值。