Java 8 中的方法参考

Method reference in Java 8

public class Car {

    private int maxSpeed;

    public Car(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }
}

我们可以对汽车列表进行排序,

    Car carX = new Car(155);
    Car carY = new Car(140);

    List<Car> cars = new ArrayList<>();
    cars.add(carX);
    cars.add(carY);

    cars.sort(Comparator.comparing(Car::getMaxSpeed));

如果我们看到方法的签名Comparator.comparing,输入参数类型是Function<? super T, ? extends U>

在上面的示例中,如何将 Car::getMaxSpeed 强制转换为 Function<? super T, ? extends U> 而以下内容无法编译?

  Function<Void, Integer> function = Car::getMaxSpeed;

那是因为getMaxSpeed方法是一个Function<Car, Integer>

即:

<Car, Integer> Comparator<Car> java.util.Comparator.comparing(
    Function<? super Car, ? extends Integer> keyExtractor
)

备注

为了从具有 :: 习语的 Car 的实例中引用 getMaxSpeed,您必须声明一个带有签名的 Car 方法:getMaxSpeed(Car car).

如果要为不带参数的方法(例如已绑定到实例的方法)创建方法引用,则应使用 Supplier,而不是 Function:

Function<Car, Integer> f1 = Car::getMaxSpeed;

Car carx = new Car(42);
Supplier<Integer> f2 = carx::getMaxSpeed; 

在方法引用 carX::getMaxSpeed 中,函数的 "implicit" this 参数已经绑定到 carx,所以你剩下一个无参数-function(顺便说一句,不能在 Comparator 中使用),而在 Java 8 中,无参数函数只是 Supplier.

同样,如果你有一个 returns void 的方法,你最终会得到一个 Comsumer:

Consumer<Integer> f3 = carx::setMaxSpeed;

作业:

Function<Void, Integer> function = carX::getMaxSpeed;

无法编译,因为它是 Supplier<Integer>,而不是 Function

那么,为什么会编译呢?:

Comparator.comparing(Car::getMaxSpeed)

Java 8 允许在需要 Function<T, U> 的地方提供 Supplier<U> 的实例方法引用,并且编译器有效地将 getter 方法转换为一个函数。

为了找出为什么这是可能的,让我们看看我们如何使用反射调用 getter 方法:

System.out.println(Car.class.getMethod("getMaxSpeed").invoke(carX)); // "155"

在实例方法上调用 invoke() 时,我们将实例传递给 getter 的 Methodinvoke() 方法 - 有一个隐含的参数实例类型。当这样看时,我们看到在引擎盖下 getter 实际上是通过 invoke() 方法实现为 Function<T, U>

一个没有参数的成员函数实际上有一个隐藏参数,this引用。 ClassName::memberFunction 形式的方法引用始终使用 class 实例的功能类型的第一个参数,即实例的隐藏 this 参数。因此,在 Car.getMaxSpeed() 的情况下,它在内部具有与 static Integer getMaxSpeed(Car car) 相同的参数。 Car::getMaxSpeed 因此适合函数类型 Function<Car,Integer>,就像 static Integer getMaxSpeed(Car car) 一样。

带有一个参数的成员函数也会发生类似的情况——它们符合 BiFunction 函数类型,第一个参数是 class 实例。

让我们详细看一下Function

Interface Function<T,R> {

    default <V> Function<T,V>   andThen(Function<? super R,? extends V> after){}

    R   apply(T t);

    default <V> Function<V,R>   compose(Function<? super V,? extends T> before){}

    static <T> Function<T,T>    identity();

}

注意 R apply(T t); Applies this function to the given argument.

Function<Void, Integer> function = Void::?????;
Void voidInstance = null;
function.apply(voidInstance);

这没有意义。您想传递一个 Void 以便应用 Void 的功能?

什么是函数编译的一些说明性示例

请注意,如果方法是 instanceMethod,c->c.getMaxSpeed()Car::getMaxSpeed 在语法上是等价的。对于非静态方法,第一个参数是从使用方法的类型推断出来的,需要稍后提供(作为方法将在 on/applied 上执行的实例)。

public class Car {

    private int maxSpeed;

    public Car(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public int getMaxSpeed() {
        return this.maxSpeed;
    }
    public Void setMaxSpeed() {
        this.maxSpeed = 12;
        return null;
    }
    public static int intStaticFunction(Void v) {
        return new Random().nextInt();
    }
    public static Void voidStaticFunction(Void v) {
        return null;
    }
    public static void main(String[] args) {
        final Car carX = new Car(155);
        final Car carY = new Car(140);

        final List<Car> cars = new ArrayList<>();
        cars.add(carX);
        cars.add(carY);

        cars.sort(Comparator.comparing(Car::getMaxSpeed));
        final Function<Car, Integer> function1 = c->c.getMaxSpeed();
        final Function<Car, Integer> function2 = Car::getMaxSpeed;
        final Function<Car, Void> function3 = Car::setMaxSpeed;
        final Function<Void, Void> function4 = n->n;
        final Function<Void, Integer> function5 = n->5;
        final Function<Void, Integer> function6 = Car::intStaticFunction;
        final Function<Void, Void> function7 = Car::voidStaticFunction;
        final Function<Car, Integer> function8 = function1::apply;
        final Function<Car, Integer> function9 = function2::apply;
        System.out.println(function1.apply(carX));
        System.out.println(function2.apply(carX));
        System.out.println(function8.apply(carX));
        System.out.println(function9.apply(carX));
        System.out.println(function3.apply(carX));
        System.out.println(function1.apply(carX));
        System.out.println(function2.apply(carX));
        System.out.println(function8.apply(carX));
        System.out.println(function9.apply(carX));
        System.out.println();
        System.out.println(function4.apply(null));
        System.out.println(function5.apply(null));
        System.out.println(function6.apply(null));
        System.out.println(function7.apply(null));
    }
}