1. 什么是方法引用?

    方法引用是java8 新增的一种引用类型,java8之前,只有对象引用和java基本类型的引用。说的简单一点就是增加了一种引用类型,和java8之前的对象引用实质是一样的。

2. 为什么要增加方法引用?

    主要目的是为了增加代码的可读性(easy to read)和简洁性(compact)。

    我们可以看一个例子,拿java集合中的排序方法来说。

    首先假设我们有一个Person对象的list, Person对象有一个生日的属性,我们以出生的先后顺序对Person List排序。

3. Java引用是如何进化的?

参考文章:http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

我们以数组的排序方法为例,看一下,一个我们熟知的方法是怎么一步一步被写成方法引用的形式的。

假设我们有一个Person对象的数组,我们要把Person对象的数组按照出生年月进行排序。

Person类如下:

public  class  Person {
     String name;
     LocalDate birthday;
  
     public  Calendar getBirthday() {
         return  birthday;
     }    
     public  static  int  compareByAge(Person a, Person b) {
         return  a.birthday.compareTo(b.birthday);
     }
}


Person对象的数组如下: 

Person[] rosterAsArray = roster.toArray( new  Person[roster.size()]);



Step 1:Java8之前的写法

java8之前,方法的参数列表是不能引用方法的,只能引用对象或者基本数据类型。

使用Array的sort方法进行排序,sort方法第二个参数需要一个继承Comparator接口的对象。

Comparator接口的类PersonAgeComparator.

class  PersonAgeComparator  implements  Comparator<Person> {
     public  int  compare(Person a, Person b) {
         return  a.getBirthday().compareTo(b.getBirthday());
     }
}
Arrays.sort(rosterAsArray,  new  PersonAgeComparator());


Step 2:用java8新特性Lambda表达式实现

Comparator是一个函数接口(functional interface)。我们就可以用Lambda表达式来实现这个接口。

Arrays.sort(rosterAsArray,
     (Person a, Person b) -> {
         return  a.getBirthday().compareTo(b.getBirthday());
     }
);

 在Lambda表达式中比较语句是:


return a.getBirthday().compareTo(b.getBirthday()); 其实这个我们已经在Person类中有实现的方法。所以Lambda表达式也可以这么写:

Arrays.sort(rosterAsArray,
     (a, b) -> Person.compareByAge(a, b)
);


Step3:方法引用(Method reference)实现

Lambda表达式中引用了一个已经存在的方法,这个方法也可以用方法引用简写成下面的形式。

Arrays.sort(rosterAsArray, Person::compareByAge);


从上面一步一步走下来,我们可以把方法引用理解成为是Lambda表达式的简写形式。


4. 什么样的方法才能使用方法引用?

一个方法要调用另外一个方法,这个方法签名中的参数必须是一个函数接口(Functional Interface).

IntBinaryOperator 接口来说。

IntBinaryOperator 这个接口就是一个函数接口,看一下这个接口的定义,只有一个方法,符合函数接口的定义。

@FunctionalInterface
public  interface  IntBinaryOperator {
  
     /**
      * Applies this operator to the given operands.
      *
      * @param left the first operand
      * @param right the second operand
      * @return the operator result
      */
     int  applyAsInt( int  left,  int  right);
}


所以oper这个方法在使用的地方就能够传入一个方法来进行调用。

Example ex =  new  Example();
ex.oper(Example::mul,  1 ,  2 );


 这个过程可以这么简单的来理解,目前这方面的java教程还是比较少的。在java8之前,引用一个带有接口定义的方法,被引用的方法必须传入一个实现了这个接口的对象。如果这个接口中有两个方法,实现类的必须把这两个方法都进行实现,才完成了对接口的实现。Java8的方法引用实质上也是对这个接口的实现,只不过是比较简单的写法。所以方法引用的语法格式只能是只含有一个未实现方法的接口即函数接口。


5. 方法引用的语法格式是什么样的?方法引用有哪些类型?

语法格式: 方法引用是用::这个符号来实现的

方法引用共有四种类型,我们举例子来说明。

publicclassExample {
  
     publicintadd(inta, intb) {
         returna + b;
     }
  
     publicstaticintmul(inta, intb) {
         returna * b;
     }
  
     publicString lower(String a) {
         returna.toLowerCase();
     }
  
     publicvoidprintDate(Date date) {
         System.out.println(date);
     }
  
     publicvoidoper(IntBinaryOperator operator, inta, intb) {
         System.out.println(operator.applyAsInt(a, b));
     }
  
     publicvoidoperS(Function<String, String> stringOperator, String a) {
         System.out.println(stringOperator.apply(a));
     }
  
     publicGregorianCalendar operC(Supplier<GregorianCalendar> supplier) {
         returnsupplier.get();
     }
  
}


类型

Lambda表达式

方法引用

说明

类名直接引用一个静态方法(Reference to a static method)

ex.oper((a,b)->Example.mul(a, b), 1, 1);

ex.oper(Example::mul, 1, 2)

Example是上面示例的类名,mul是其一个静态方法。

对象引用非静态方法(Reference to an instance method of a particular object)

ex.oper((a, b)->ex.add(a, b), 1, 2)

ex.oper(ex::add, 1, 2)

ex是上面示例类的一个对象,add是其实例方法。

对象引用一个非静态方法(Reference to an instance method of an arbitrary object of a particular type)

ex.oper(s->s.toLowerCase(), "STRING")

ex.oper(String::toLowerCase, "STRING")

用类名直接引用实例方法,toLowerCase是String类的一个示例方法。

其实,在方法调用的过程中,已经传送了一个String的对象。

引用构造方法(Reference to a constructor)

ex.operC(()-> {return new GregorianCalendar();})

ex.oper(GregorianCalendar::new)

这个很简单,其实就是构造方法的简写。