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

>前言

首先,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 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;
	}
}
输出:
[Java]“语法糖”系列(一)之方法引用(Method References)_第1张图片


>实例解析

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

    default void forEach(Consumer 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 {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
可以近似认为是一个专供函数式编程(此处暂时缩小范围为:方法引用中的“构造引用”)使用的工厂方法。



你可能感兴趣的:(JAVA)