Java8中的方法引用

在上一篇的Java8新特性中我们聊过Lambda表达式,这一篇我们来看一下Java8中另一个特性:方法引用

方法引用

 

在讲方法引用之前我们先看一个例子以及输出的结果:

 public static void LambdaTest() {
 List list = Arrays.asList("Lambdas",
            "Default Method",
            "Stream API",
            "Date and Time API");
 list.forEach(n -> System.out.println(n));
 list.forEach(System.out::println);
    }

结果:

Connected to the target VM, address: '127.0.0.1:51219', transport: 'socket'
Lambdas
Default Method
Stream API
Date and Time API
Lambdas
Disconnected from the target VM, address: '127.0.0.1:51219', transport: 'socket'
Default Method
Stream API
Date and Time API

Process finished with exit code 0

我们可以看到他们的输出结果是一样的。第一种格式的输出我们在讲解Lambda的时候已经看到过,但是第二种你是否看过?同时你是知道::这个符号代表什么意思呢?我们慢慢来看:

今天要说的方法引用就是上面代码第二种格式,在上一篇关于Lambda的文章中,我们说Lambda在一定条件下能代替匿名类,但是有时候Lambda表达式中一些代码仅仅是调用了一个已存在的方法(可以理解成访问的类或者实例已经存在,而这个我们去调用它的方法或者构造方法)。我们可以使用形如System.out::println这种方式去代替n -> System.out.println(n);而这种特性就叫做方法引用。我们方法引用的操作符就是双冒号"::"。

此时你是不是很好奇我们什么时候使用方法引用呢?以及我们如何知道我们要用这用形式呢?

我们继续看一个例子:

先定义一个函数式接口:

@FunctionalInterface
interface RefenceTest {
    public abstract String refence(String n);

}

一个引用方法(通过对象名引用成员方法,MethodRefence是方法所在的类名

PrintString和refencePlug是MethodRefence类的一个方法(虽然没带括号,下面我们会展示这个方法),这就好像是重写了上面接口中的refence方法):

  public static void refenceImp() {
/*
通过对象名引用成员方法
*/
 MethodRefence methodRefence = new MethodRefence();
 PrintString(methodRefence::refencePlug);
}

MethodRefence就是这个方法所在的类名,而PrintString这个方法是这样的:

    public static void PrintString(RefenceTest s) {
        s.refence("每天学Java");
    }

方法的参数中是我们的函数式接口的引用,如果不知道Java8方法引用的情况下,我们要想使用接口的方法,我们必须用一个实现它的类或者匿名类或者Lambda:

//匿名类
PrintString(new RefenceTest() {
@Override
 public String refence(String n) {
 System.out.println("每天学Java");
 return "";
}
});
 //Lambda
 RefenceTest refenceTest = n -> n;
 System.out.println(
 refenceTest.refence("每天学Java")
 );

 

但是我们上面说了,如果一个类的实例的方法存在,我们就可以通过方法引用实现相同的效果:

    public String refencePlug(String n) {
        System.out.println(n);
        return "";
    }

这个refencePlug方法我们就可以当成实例方法的存在(注意:它的返回类型要和函数式接口的返回相同,且不能是静态的,因为接口中的方法默认是abstract的,不能和static同时修饰)

在这种情况下:我们就可以使用:

   public static void refenceImp() {
        /*
        通过对象名引用成员方法
         */
        MethodRefence methodRefence = new MethodRefence();
        PrintString(methodRefence::refencePlug);

    }

    public static void PrintString(RefenceTest s) {
        s.refence("每天学Java");
    }

    public String refencePlug(String n) {
        System.out.println(n);
        return "";
    }

 

如果这样一看,好像它比Lambda麻烦不少啊,我还要去自己写方法,Lambda直接就可以搞定了,我们要考虑以下几点,一.如果上面的方法是jar包中已经存在的方法,而不是你自己写的,你还觉得它麻烦嘛?二.如果这个接口的方法在几十个或者上百个类中使用过,你在每一个类中调用Lambda少写代码,还是定义一个类用于方法引用写的代码少呢?就像下面一样:

 

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

  list.forEach(System.out::println);

那么说了上面的例子,我们现在来描述一下方法引用:当我们使用双冒号表示方法引用时候,就会创建一个函数式接口的实例(可以理解为实现了这个接口,并重写了它的方法的实例)。 简单地说,Java 8中,我们使用Lambda表达式创建匿名方法,有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。

不知道说了这些之后,你是否对于方法引用有一些理解,希望各位小伙伴可以自己敲代码体验以下。下面我附上自己写的几个小例子。

 

02

小例子

 

1.函数式接口的静态方法如何调用:

@FunctionalInterface
interface RefenceTest {
    public  abstract   String refence(String n);

    public static void staticMethod(String n){
        System.out.println(n);
    }

}

希望你们看到如何调用后,不要骂我骗子,你这分明是凑例子吗(嘿嘿,其实静态方法还用问怎么调用吗?不过不知道大家时候知道Java8中的接口允许写方法的)

 public static void main(String[] args) {
        RefenceTest.staticMethod("赶快来关注每天学Java吧");
    }

2.通过类名引用静态方法(RefenceTest接口代码和上面相同,MethodRefence就是这个类的类名)

    /*
     通过类名引用静态方法
     */
    public static String getSplit(RefenceTest refenceTest) {
        refenceTest.refence("这里有许多Java相关的文章哦");
        return "";
    }

    public static String SpiltPlug(String y) {
        System.out.println(y);
        return "";
    }
    
 public static void main(String[] args) {
      getSplit(MethodRefence::SpiltPlug);
    }

    

3. 通过类名引用成员方法,不用我强调类名和对象名的区别吧(类名 对象名=new 类名();)。这里我们虽然说是使用类名去引用成员方法,但是实际上我们还是传入了对象名,只是写的方式不一样而已

     public void class0Method(String x) {
        System.out.println(x);
    }

    public static void class0Me(RefenceTest2 refenceTest2,
         MethodRefence methodRefence, 
         String x) {
        refenceTest2.refence(methodRefence, x);
    }
    
     public static void main(String[] args) {
     class0Me(MethodRefence::class0Method,
     new MethodRefence(), 
     "我都标红了,你为啥子还不关注每天学Java");    }

 

 

好了,这篇文章就到这里结束了,大家有任何问题可以关注公众号留言哦。明天推送的是Effective Java第三版的条目十三:谨慎地重写 clone 方法。同时每天学Java小程序题库更新了:关于MQ的一些面试题(或者说是知识点)

你可能感兴趣的:(Java8中的方法引用)