Java中的方法参考
在Java的后Lambda表达式中,我们已经看到Lambda表达式如何提供功能接口的实例并实现功能接口的抽象方法。尽管有时,lambda表达式仅用于调用现有方法。在这种情况下,我们可以使用Java中的方法引用按名称引用现有方法。方法引用是一种紧凑且可读性强的lambda表达式,用于具有名称的方法。
例如,考虑以下lambda表达式
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); list.forEach((Integer a) -> System.out.println(a));
在这里,lambda表达式只是调用一个现有方法,可以使用方法引用来完成该方法,从而使代码更具可读性和简洁性。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); list.forEach(System.out::println);
方法参考也需要目标类型
Java方法引用可以称为lambda表达式的一种特殊形式,因为方法引用还需要目标类型上下文(兼容的功能接口),并且它也像lambda表达式一样创建功能接口的实例。这也意味着方法引用只能用于单个方法。
两者之间的区别在于,lambda表达式还可以为抽象方法提供实现,其中方法引用指的是现有方法。
Java方法参考语法
正如我们在示例中已经看到的那样,在Java中添加了一个新的双冒号运算符(::)以与方法引用一起使用。
包含方法的类或者对象位于双冒号运算符的左侧,而方法的名称位于运算符的右侧。
方法参考种类
Java中有四种方法参考
| 种类 | 示例 |
| 参考静态方法 | ContainingClass :: staticMethodName |
| 引用特定对象的实例方法 | containsObject :: instanceMethodName` |
| 参考特定类型的任意对象的实例方法 | ContainingType :: methodName |
| 参考构造函数 | ClassName :: new |
静态方法参考
下面的示例演示如何在Java中使用静态方法引用。
public class MethodRef {
public static <T> List<T> filter(List<T> myList, Predicate<T> predicate) {
List<T> filterdList = new ArrayList<T>();
for(T element: myList) {
if(predicate.test(element)) {
filterdList.add(element);
}
}
return filterdList;
}
public static boolean isMoreThanValue(int i) {
return i > 10;
}
public static void main(String[] args) {
List<Integer> myList = Arrays.asList(25, 5, 17, 1, 7, 14, 9, 11);
System.out.println("Method call as lambda expression");
List<Integer> filterdList = filter(myList, (i) -> MethodRef.isMoreThanValue(i));
System.out.println("Filtered elements- " + filterdList);
System.out.println("Method call using method reference");
filterdList = filter(myList, MethodRef::isMoreThanValue);
System.out.println("Filtered elements- " + filterdList);
}
}
输出:
Method call as lambda expression Filtered elements- [25, 17, 14, 11] Method call using method reference Filtered elements- [25, 17, 14, 11]
在示例中,使用方法参考MethodRef :: isMoreThanValue调用了isMoreThanValue静态方法。
在filter方法中,参数之一是Predicate类型。谓词是一个具有抽象方法test()的功能接口,该方法对给定参数评估此谓词并返回布尔值(true或者false)。
静态方法isMoreThanValue()是谓词功能接口的抽象方法test()的实现。当我们进行方法调用filter(myList,MethodRef :: isMoreThanValue)时,Java可以从上下文中推断isMoreThanValue()是Predicate接口的实现。
对实例方法的方法引用
在这种情况下,我们可以使用类的对象来引用方法,而不是使用类名。
public class MethodRef {
public <T> List<T> filter(List<T> myList, Predicate<T> predicate) {
List<T> filterdList = new ArrayList<T>();
for(T element: myList) {
if(predicate.test(element)) {
filterdList.add(element);
}
}
return filterdList;
}
public boolean isMoreThanValue(int i) {
return i > 10;
}
public static void main(String[] args) {
List<Integer> myList = Arrays.asList(25, 5, 17, 1, 7, 14, 9, 11);
MethodRef obj = new MethodRef();
System.out.println("Method call as lambda expression");
List<Integer> filterdList = obj.filter(myList, (i) -> obj.isMoreThanValue(i));
System.out.println("Filtered elements- " + filterdList);
System.out.println("Method call using method reference");
filterdList = obj.filter(myList, obj::isMoreThanValue);
System.out.println("Filtered elements- " + filterdList);
}
}
输出:
Method call as lambda expression Filtered elements- [25, 17, 14, 11] Method call using method reference Filtered elements- [25, 17, 14, 11]
这是与静态方法引用相同的示例,只是更改,现在将类的实例用于方法引用。现在也不要求方法是静态的。
引用特定类型的任意对象的实例方法
在前面的示例中,使用了类的特定对象,但是我们可能会遇到一种情况,在这种情况下,我们需要指定可以与给定类的任何对象一起使用的实例方法。在这种情况下,方法引用将具有以下形式:
ClassName::instanceMethodName
在这种情况下,功能接口的第一个参数与用于调用该方法的对象匹配,并且其他任何参数都将传递给该方法。
在示例中,有一个类Person,其字段firstName,lastName,age,并且我们需要获取年龄大于50的Person的计数。在这种情况下,必须为所有Person对象调用isAgeGreater()方法。
class Person {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age){
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
// This becomes the abstract method (test) implementation
// of the functional interface
boolean isAgeGreater(int age) {
return this.getAge() > age;
}
}
@FunctionalInterface
interface TestInterface {
boolean test(Person person, int age);
}
public class MethodRef {
public static void main(String[] args) {
List<Person> tempList = createList();
int count = ageCounter(tempList, Person::isAgeGreater, 50);
System.out.println("Person count age greater than 50- " + count);
}
static int ageCounter(List<Person> list, TestInterface ref, int age) {
int count = 0;
for(Person person : list) {
// first param becomes the invoking object
// other parameters are passed to method
if(ref.test(person, age))
count++;
}
return count;
}
private static List<Person> createList(){
List<Person> tempList = new ArrayList<Person>();
tempList.add(new Person("Joe","Root", 28));
tempList.add(new Person("Mathew","Hayden", 42));
tempList.add(new Person("Richard","Hadlee", 55));
tempList.add(new Person("Sunil","Gavaskar", 65));
tempList.add(new Person("Brian","Lara", 45));
return tempList;
}
}
构造函数参考
我们还可以引用与方法引用类似的构造函数,只是在这种情况下,方法的名称是新的。
构造函数参考的语法如下:
classname::new
构造函数参考Java示例
在copyElements方法中,参数之一是Supplier类型,它是java.util.function包中定义的功能接口。功能接口Supplier包含一个方法get,该方法不带任何参数并返回一个对象。新的ArrayList实例作为构造函数引用传递给Supplier。
public class MethodRef {
public static void main(String[] args) {
List<Integer> myList = Arrays.asList(25, 5, 17, 1, 7, 14, 9, 11);
List<Integer> tempList = copyElements(myList, ArrayList::new);
System.out.println("Copied list- " + tempList);
}
public static List<Integer> copyElements(List<Integer> sourceList, Supplier<List<Integer>> destList) {
List<Integer> list = destList.get();
for (Integer i : sourceList) {
list.add(i);
}
return list;
}
}

