Java 8 引入了一个新的概念:Method Reference表达式(Method Reference Expression)。在以前的版本中,Java语言无法抽象一个方法。例如:将方法赋值给一个变量,或者将方法作为参数传入另一个方法。在C语言中,这些可以通过函数指针实现。然而,Java语言采用了不同的实现方式和策略。在本章中,我们先介绍Method Reference。Method Reference为我们划定了哪些方法是可以作为一等公民(First-Class Citizen)的。在Method Reference和Functional Interface的协助下,Java编译器能够将一个函数转化为一个Functional Interface的对象,从而,在Java语言上,开发人员能像使用其他类对象那样传递函数了。
就像在Java标准文档(Java SE Specification)中介绍的那样,Method Reference抽象的是一个方法调用,但是,这个方法调用并没有触发真正的方法调用。这句话有点难于理解,不过,简单地说,Method Reference提供了一个方法调用的环境(或者上下文)。Method Reference与真实的方法调用一模一样,唯一的区别是,Method Reference并没有发生方法调用。但是,当Method Reference被方法时,就像它在原处被方法的一样。
A method reference expression is used to refer to the invocation of a method without actually performing the invocation. -- The Java Language Specification, Java SE 13 Edition
举例是一个很好的帮助理解Method Reference表达式的办法。从用法上看,Method Reference可分为四类。
import java.util.Arrays;
public class Vehicle {
protected Integer price;
public static int comparePrice(Vehicle v1, Vehicle v2) {
return v1.price.compareTo(v2.price);
}
public static void main(String[] args) {
Vehicle[] vehicles = {};
Arrays.sort(vehicles, Vehicle::comparePrice);
}
}
import java.util.Arrays;
public class VehicleComparisonProvider {
public int compareByPrice(Vehicle v1, Vehicle v2) {
return v1.getPrice().compareTo(v2.getPrice());
}
public int compareByAge(Vehicle v1, Vehicle v2) {
return v1.getAge().compareTo(v2.getAge());
}
public static void main(String[] args) {
Vehicle[] vehicles = {};
VehicleComparisonProvider myProvider = new VehicleComparisonProvider();
Arrays.sort(vehicles, myProvider::compareByPrice);
}
}
当在成员方法中将另一个成员方法或者父类的成员方法作为Method Reference传递时,可使用关键字this和super指定所绑定的对象。如下例所示。ChildVehicleComparisonProvider类继承自VehicleComparisonProvider。在其成员方法sortByBrand中,如果使用当前类的compareByBrand成员方法作为比较方法,可将this::compareByBrand传递给Arrays.sort函数。这个用法与上例中的myProvider::compareByPrice类似。唯一不同的是,本例中的Method Reference绑定的是this指向的对象。
同理,当需要传递在父类中定义的成员方法时,可使用super::compareByPrice或者ChildVehicleComparisonProvider.super::compareByPrice作为Method Reference传递。super指向的是当前的父类对象。Java语言要求当使用 类名.super::函数名 格式时,类名必须是当前的类名,即包含super::函数名这个表达式的类名。
此类Method Reference的使用方法(或者格式)不能用于静态函数。
import java.util.Arrays;
public class ChildVehicleComparisonProvider extends VehicleComparisonProvider {
public void sortByBrand(Vehicle[] vehicles) {
Arrays.sort(vehicles, this::compareByBrand);
}
public int compareByBrand(Vehicle v1, Vehicle v2) {
return v1.getBrand().compareTo(v2.getBrand());
}
public void sortByPrice(Vehicle[] vehicles) {
Arrays.sort(vehicles, super::compareByPrice);
Arrays.sort(vehicles, ChildVehicleComparisonProvider.super::compareByPrice);
}
}
import java.util.Arrays;
public class Vehicle {
protected Integer price;
public Vehicle(Integer price) {
this.price = price;
}
public int comparePrice (Vehicle v) {
return this.price.compareTo(v.price);
}
public static void main(String[] args) {
Vehicle[] vehicles = {new Vehicle(100), new Vehicle(200)};
Arrays.sort(vehicles, Vehicle::comparePrice);
}
}
指向构造函数 的Method Reference(Reference to a constructor) 指向构造函数的Method Reference的用法与指向静态函数的用法类似,只不过,因为构造函数没有名字,所以,关键字"new"特指构造函数。如下例所示,这是一个Vehicle工厂类的例子。在VehicleFactory中,Vehicle::new作为指向Vehicle构造函数的Method Reference传入了newVehicle函数。
Method Reference还可用于数组的构造函数。例如,newVehicleArray函数接受一个创建Vehicle数组的工厂函数或者构造函数,size参数指明数组的长度。Vehicle[]::new是这个数组构造函数的Method Reference。Java编译器将其转换成一个functional interface对象,传入newVehicleArray函数。
import java.util.function.Function;
public class VehicleFactory {
public static Vehicle newVehicle(Function<Integer, Vehicle> factory, Integer price) {
return factory.apply(price);
}
public static Vehicle[] newVehicleArray(Function<Integer, Vehicle[]> factory, Integer size) {
return factory.apply(size);
}
public static void main(String[] args) {
Vehicle i = VehicleFactory.newVehicle(Vehicle::new, 100);
Vehicle[] vehicles = VehicleFactory.newVehicleArray(Vehicle[]::new, 10);
}
}
Method Reference的处理过程非常复杂,在这里,我们仅仅大致介绍一下思路。Method Reference的处理过程可分为三个步骤。
Java 8为了更好的支持函数式编程,推出了Method Reference表达式、Lambda表达式和Functional Interface的概念。Method Reference的设计目的是为了将类继承框架下的函数/方法引入函数式编程。Method Reference表达式支持静态函数、成员函数、和构造函数。因此,在函数式编程思想下,开发人员也能复用这些函数,为打通面向对象编程和函数式编程打下了基础。
注册用户登陆后可留言