您的位置:首页 > Web前端

[Java]“语法糖”系列(一)之方法引用(Method References)

2017-11-23 22:44 351 查看
>前言

首先,JAVA的方法引用需要在JDK8以上才能运作,因为它是JDK8引入的新特性。

JAVA经过这么多版本的迭代,已经跟几十年前的C++之流完全不同了;在JAVA中,引入了很多更抽象的语言特性,比如Lambda、迭代器、方法引用之类的,有人视之为语法糖,因为这些新的高级语言特性确实精简了编写代码时的工作量、也使得整体代码更加易读(前提是你学习了这些语言特性)。Anders Hejlsberg就提到过编程语言在未来会逐渐分化并融合各自的特性,在传统的声明式语言上更加抽象化地加入函数式编程方法。

>方法引用

方法引用的用处就是在Lambda表达式中进一步精简代码和突出代码行为,是函数式编程思想的另一体现。

主要有四类:

引用类型引用示例
构造方法$ClassName::new
实例方法$instance::$methodName
类任意对象实例$ClassName::$methodName
类静态方法$ClassName::$staticMethodName
下面我们将给出一个例子,来对这四种方法引用做介绍。

>一个实例

public class Sugar {

public static void main(String[] args){
Person p0 = new Person("Teacher");
Person p1 = new Person("Tom");
Person p2 = new Person("Jam");
List<Person> list = Arrays.asList(p1,p2);
list.forEach(Person::sayHello);//(x)->Person.sayHello(x)
list.forEach(Person::total);//(x)->x.total()
list.forEach(Person::sayGoodbye);//(x)->x.sayGoodbye()
list.forEach(p0::sayGoodbyeTo);//(x)->p0.sayGoodbyeTo(x)

Person p3 = Person.create("Baby",Person::new);
p3.sayGoodbye();
}

}
class Person{
private static int sum = 0;
private String name;
public Person(){}//必须给出一个无参构造函数才能使用‘构造引用’
public Person(String name){
this.name = name;
Person.sum++;
}
//针对list.forEach(Consumer)做1~3,已知forEach的动作是每次向Consumer传递一个list中的对象
//1 静态 带参 Person::sayHello
public static void sayHello(Person p){
System.out.println(p.name + " Say Hello!");
}
//2.1 非静态 不带参 Person::total
public void total(){
System.out.println("Sum:"+Person.sum);
}

//2.2 实例方法(非静态) 不带参 Person::sayGoodbye
public void sayGoodbye(){
System.out.println(this.name + " Say Goodbye!");
}
//3 实例方法(非静态) 带参 p0::sayGoodbyeTo
public void sayGoodbyeTo(Person p){
System.out.println(this.name + " Say Goodbye To "+p.name);
}

//4.引用构造方法
public static Person create(String name,Supplier< Person > supplier){
Person p = supplier.get();
p.name = name;
return p;
}
}
输出:



>实例解析

首先,我们要先了解Arrays.forEach方法:

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) { //依次给出本list中的每一个listItem
action.accept(t);
}
}
注意到,forEach对给定的动作(Consumer action),依次给出本list中的每一个listItem,调用该方法,并传入该listItem作为唯一的参数,去执行这个动作。这个动作(行为),就是一个函数,是函数式编程思想的体现。

也就是,上文实例代码中的这一行:

list.forEach(Person::sayHello);
实际上静态方法sayHello收到了一个Person实例作为输出,并执行了sayHello方法。如果我们把原本的静态带参(一个)函数sayHello方法改成:

public void sayHello(Person p){
System.out.println(p.name + " Say Hello!");
}
也就是非静态带参函数,那么编译器就会报错。结合实例代码中的3,可以很容易理解其报错的原因:forEach传入了一个作为参数的Person却找不到一个作为执行实例的Person(因为此时不是静态方法了)。

同样的道理,可以去对比2.2和3、2.1和2.2,很容易就能看懂什么时候要用静态引用、什么时候可以带参。

还需要注意的是:必须给出一个无参构造函数才能使用‘构造引用’

我在实例代码中还给出了一个静态的create方法,该方法中的Supplier接口是一个函数式接口:

@FunctionalInterface
public interface Supplier<T> {

/**
* Gets a result.
*
* @return a result
*/
T get();
}
可以近似认为是一个专供函数式编程(此处暂时缩小范围为:方法引用中的“构造引用”)使用的工厂方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息