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; } }