1 什么是方法/构造器引用

简单来说,方法引用是对lambda表达式的一种更加简便的写法。

所谓引用,就是对当前已存在的一段代码的借用。

方法引用则是隐式借用已经存在的方法作为现成的执行逻辑,而不必在lambda表达式中显示调用该方法,或者重写这一部分代码

同理,构造器引用则是隐式借用某个类的构造函数创建对象的执行逻辑。

2 举个简单的例子

假设有以下方法,它接收一个泛型参数t,然后使用Comsumer函数式接口进行动态处理:

public class FI1 {
    public static <T> void accept(T t, Consumer<T> consumer) {
        consumer.accept(t);
    }
}

假如,我们只需要简单打印一个字符串,可以按照如下方式进行显示调用引用的println()方法:

FI1.accept("Xianhuii", t -> {
    System.out.println(t);
});

或者按照如下方式进行重写代码

FI1.accept("Xianhuii", t -> {
    PrintStream out = System.out;
    synchronized (out) {
        out.print(t);
        out.print("\n\b");
    }
});

我们可以查看Consumer#accept方法的格式:

void accept(T t);

以及System.out.println()方法的格式:

public void println(String x) {}

发现它俩的形参数量和返回值类型都一致。同时,我们需要的业务逻辑与println()方法定义的一致。

因此,我们可以直接引用该方法:

FI1.accept("Xianhuii", System.out::println);

相当于将System.out.println(String)方法等价替换了accept(T)方法,并使用了现有的执行逻辑。

3 方法引用的格式

方法引用可以分成三种情况:

  1. 对象::实例方法
  2. 类::静态方法
  3. 类::实例方法

这三种情况也分别对应着不同格式(形参和返回值)的lambda表达式/函数式接口方法。

但是我们需要注意:任何情况下,被引用方法的执行逻辑必须与lambda表达式中的执行逻辑一致。

3.1 对象::实例方法

对象::实例方法引用的是对象的方法,::前表示被引用的对象,::后表示被引用对象的实例方法。

3.1.1 Consumer

函数式接口:

public interface Consumer<T> {
    void accept(T t);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
}

方法引用:

obj::accetp

被引用方法:

public void accept(T t) {
    // 相同的业务逻辑
}

3.1.2 Function

函数式接口:

public interface Function<T, R> {
    R apply(T t);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
    return r;
}

方法引用:

obj::apply

被引用方法:

public R apply(T t) {
    // 相同的业务逻辑
    return r;
}

3.1.3 Supplier

函数式接口:

public interface Supplier<T> {
    T get();
}

lambda表达式:

() -> {
    // 相同的业务逻辑
    return t;
}

方法引用:

obj::get

被引用的方法:

public T get() {
    // 相同的业务逻辑
    return t;
}

3.1.4 Predicate

函数式接口:

public interface Predicate<T> {
    boolean test(T t);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
    return flag;
}

方法引用:

obj::test

被引用的方法:

public boolean test(T t) {
    // 相同的业务逻辑
    return flag;
}

3.2 类::静态方法

类::静态方法引用的是类的静态方法,::前表示被引用的类,::后表示被引用类的静态方法。

类::静态方法的转换方式与对象::实例方法相同,只是引用对象和引用方法有所区别。

3.2.1 Consumer

函数式接口:

public interface Consumer<T> {
    void accept(T t);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
}

方法引用:

Clz::accetp

被引用方法:

public static void accept(T t) {
    // 相同的业务逻辑
}

3.2.2 Function

函数式接口:

public interface Function<T, R> {
    R apply(T t);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
    return r;
}

方法引用:

Clz::apply

被引用方法:

public static R apply(T t) {
    // 相同的业务逻辑
    return r;
}

3.2.3 Supplier

函数式接口:

public interface Supplier<T> {
    T get();
}

lambda表达式:

() -> {
    // 相同的业务逻辑
    return t;
}

方法引用:

Clz::get

被引用的方法:

public static T get() {
    // 相同的业务逻辑
    return t;
}

3.2.4 Predicate

函数式接口:

public interface Predicate<T> {
    boolean test(T t);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
    return flag;
}

方法引用:

Clz::test

被引用的方法:

public static boolean test(T t) {
    // 相同的业务逻辑
    return flag;
}

3.3 类::实例方法

类::实例方法本质上引用的是对象的实例方法,只是对象是lambda表达式的第一个形参。

::前表示被引用对象所属的类,::后表示被引用对象的实例方法。

3.3.1 Consumer

函数式接口:

public interface Consumer<T> {
    void accept(T t, P p);
}

lambda表达式:

(t) -> {
    t.fun(p);
}

方法引用:

Clz::fun

被引用方法:

public void fun(P p) {
    // 相同的业务逻辑
}

3.3.2 Function

函数式接口:

public interface Function<T, R> {
    R apply(T t, P p);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
    return t.fun(p);
}

方法引用:

Clz::fun

被引用方法:

public static R fun(P p) {
    // 相同的业务逻辑
    return r;
}

3.3.3 Supplier

函数式接口:

public interface Supplier<T> {
    R get(T t);
}

lambda表达式:

() -> {
    // 相同的业务逻辑
    return t.fun();
}

方法引用:

Clz::fun

被引用的方法:

public static R fun() {
    // 相同的业务逻辑
    return r;
}

3.3.4 Predicate

函数式接口:

public interface Predicate<T> {
    boolean test(T t, P p);
}

lambda表达式:

(t) -> {
    // 相同的业务逻辑
    return t.fun(p);
}

方法引用:

Clz::fun

被引用的方法:

public static boolean fun(P p) {
    // 相同的业务逻辑
    return flag;
}

4 构造器引用的格式

构造器引用可以是一种特殊的类::静态方法引用,只不过这里的静态方法是构造函数

构造器引用的格式为:类::new

lambda表达式/函数式接口的形参,对应构造函数的形参。并且编译器会根据形参的个数和类型,推断出正确的构造函数进行引用。

由于构造函数一定会返回实例对象,所以对应lambda表达式也必须有返回值。

4.1 类::new

4.1.1 Function<T, R>

函数式接口:

public interface Function<T, R> {
    R apply(T t);
}

lambda表达式:

(t) -> {
    return new Date(t);
}

方法引用:

Date::new

被引用方法:

public Date(long date) {
    fastTime = date;
}

4.1.2 Supplier<T>

函数式接口:

public interface Supplier<T> {
    R get();
}

lambda表达式:

() -> {
    return new Date();
}

方法引用:

Date::new

被引用的方法:

public Date() {
    this(System.currentTimeMillis());
}

4.2 类[]::new

如果需要创建数组对象,可以引用数组的构造函数。

需要注意的是,此时必须传入一个参数,作为数组的长度。

此外,这种方式无法为每个值赋值,因此最终得到的数组值都为null

4.1.1 Function<T, R>

函数式接口:

public interface Function<T, R> {
    R apply(T t);
}

lambda表达式:

(t) -> {
    return new Date[t];
}

方法引用:

Date[]::new

被引用方法:

new Date[t];