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

来源:互联网 发布:qt tcp 端口监听 编辑:程序博客网 时间:2024/06/08 02:46

>前言

首先,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::sayHellopublic static void sayHello(Person p){System.out.println(p.name + " Say Hello!");}//2.1 非静态 不带参 Person::totalpublic void total(){System.out.println("Sum:"+Person.sum);}//2.2 实例方法(非静态) 不带参 Person::sayGoodbyepublic void sayGoodbye(){System.out.println(this.name + " Say Goodbye!");}//3 实例方法(非静态) 带参 p0::sayGoodbyeTopublic 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接口是一个函数式接口:

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



阅读全文
1 0