有一个重要的特性与lambda表达式相关,叫做方法引用(method reference).方法引用提供了一种引用方法而不执行方法的方式.这种特性与lambda表达式相关,这是因为他也需要由兼容的函数式接口构成的目标类型上下文,计算时,方法引用也会创建函数式接口的一个实例.方法引用的类型有许多种,我嗯首先看静态方法的方法引用.
(1)静态方法的方法引用
要创建静态方法引用,需要使用下面的一般语法,也就是在类名后跟上方法名:
className::methodName
注意,类名与方法名之间使用双冒号分隔开.::是JDK8新增的分隔符,专门用于此目的.在与目标类型兼容的任何地方,都可以使用这个方法引用.
下面的程序演示了一个静态方法引用.程序首先声明了一个函数式接口IntPredicate.该接口包含一个名为test()的方法,test()方法有一个int类型的形参并返回一个boolean类型的结果.因此,可以用于测试针对某个条件的整数值.然后程序创建了MyIntPredicates类,该类定义了三个静态方法;isPrime(),isEven()和isPositive(),他们都用于检查一个值是否满足某个条件,每个方法所执行的操作可以通过其名称看出来.在MethodRefDemo中,所创建的numTest()方法将对IntPredicate的引用作为他的第一个形参.numTest()的第二个形参指定所测试的整数.在main()中,通过调用numTest(),将方法引用传入到要执行的测试,从而执行三种不同的测试.
public class test2 { static boolean numTest(IntPredicate p,int v) { return p.test(v); } public static void main(String args[]) { boolean result; result = numTest(MyIntPredicates::isPrime,17); if(result) System.out.println("17 is prime"); result = numTest(MyIntPredicates::isEven,12); if(result) System.out.println("12 is even"); result = numTest(MyIntPredicates::isPositive,11); if(result) System.out.println("11 is positive"); } } interface IntPredicate { boolean test(int n); } class MyIntPredicates { static boolean isPrime(int n) { if(n<2) return false; for(int i=2;i<=n/i;i++) if((n%i)==0) return false; return true; } static boolean isEven(int n) { return (n%2) == 0; } static boolean isPositive(int n) { return n>0; } }
程序输出如下所示:
17 is prime 12 is even 11 is positive
在程序中,需要特别注意下面这行代码:
result = numTest(MyIntPredicates::isPrime,17);
这里,将对静态方法isPrime()的引用传递给了numTest()方法的第一个参数,这是可行的,因为isPrime与IntPredicate函数式接口兼容,因此,表达式MyIntPredicate::isPrime的计算结果为对象引用,其中isPrime提供了intPredicate的test()方法的实现.对NumTest()方法的其他两种调用方式与此相同.
(2)示例方法的方法引用
要创建对某个具体对象的实例方法的引用,需要使用下面的基本语法:
objRef::methodName
可以看到,这种语法与用于静态方法的语法类似,只不过这里使用的是对象引用,而不是类名.因此,由方法引用所引用的方法进行的操作针对的是objRef.下面的程序演示了这一点.该程序中使用的IntPredicate接口和test()方法与前面程序中的相同.但创建了MyIntNum类,该类中存储int类型的值,并定义了方法isFactor(),用于确定所传入的值是否是MyIntNum示例所存储值的因子.之后main()方法创建了两个MyIntNum实例,他调用numTest()方法,将方法应用传入isFactor()方法并传入要检查的值,在每一种情况下,方法引用的操作都会相对于具体的对象.
public class test2 { public static void main(String args[]) { boolean result; MyIntNum myNum = new MyIntNum(12); MyIntNum myNum2 = new MyIntNum(16); IntPredicate ip = myNum::isFactor; result = ip.test(3); if(result) System.out.println("3 is a factor of "+myNum.getNum()); ip = myNum2::isFactor; result = ip.test(3); if(!result) System.out.println("3 is not a factor of "+myNum2.getNum()); } } interface IntPredicate { boolean test(int n); } class MyIntNum { private int v; MyIntNum(int x){ v = x; } int getNum() { return v; } boolean isFactor(int n) { return (v%n)==0; } }
产生如下输出:
3 is a factor of 12 3 is not a factor of 16
在程序中,要特别注意如下代码行:
IntPredicate ip = myNum::isFactor;
这里,赋给IP的方法引用了myNum的实例方法isFactor(),因此,当通过该应用调用test()方法时,代码如下所示:
result = ip.test(3);
test()方法将调用myNum的isFactor(),其中isFactor()就是创建方法引用时指定的对象.对于方法应用myNum2::isFactor,则存在同样的情况,只不过调用的是myNum2上的isFactor().这一点可以通过输出来确认.
classname::instanceMethodName
这里使用了类的名称,而不是具体的对象,尽管指定的是实例方法.使用这种形式时,函数式接口的第一个形参匹配调用对象,第二个形参匹配方法指定的参数.下面是一个例子,该例重写了前面的示例.首先,他用实例MyIntNumPredicate替代了IntPredicte.在这里,test()的第一个形参的类型为MyIntNum,用于接收要操作的对象.这允许运行程序以创建对示例方法isFactor()的方法引用,其中isFactor()可以用于任何MyIntNum对象.
public class test2 { public static void main(String args[]) { boolean result; MyIntNum myNum = new MyIntNum(12); MyIntNum myNum2 = new MyIntNum(16); MyIntNumPredicate inp = MyIntNum::isFactor; result = inp.test(myNum,3); if(result) System.out.println("3 is a factor of "+myNum.getNum()); result = inp.test(myNum2,3); if(!result) System.out.println("3 is not a factor of "+myNum2.getNum()); } } interface MyIntNumPredicate { boolean test(MyIntNum mv,int n); } class MyIntNum { private int v; MyIntNum(int x){ v = x; } int getNum() { return v; } boolean isFactor(int n) { return (v%n)==0; } }
程序输出如下所示:
3 is a factor of 12 3 is not a factor of 16
在该程序中,要特别注意如下代码:
MyIntNumPredicate inp = MyIntNum::isFactor;
这行代码创建了对实例方法isFactor()的方法引用,其中isFactor()将处理MyIntNum类型的任何对象,例如,当通过inp调用test()时,diamante如下所示:
result = inp.test(myNum,3)
这行代码将调用myNum.isFactor(3),换言之,myNum变成了在其中调用isFactor(3)的对象.
问:如何指定对泛型方法的方法引用?
答:通常,因为使用类型推断,所以在获得泛型方法的方法引用时,不必显式指定泛型方法的类型实参,但Java中确实包含了处理这类情况的语法,例如,如下代码:
interface SomeTest<T> { boolean test(T n,T m); } class MyClass { static<T> boolean myGenMeth(T x,T y) { boolean result = false; return result; } }
下面语句是有效的:
SomeTest<Integer> mRef = MyClass::<Integer>myGenMeth;
这里显式指定了泛型方法myGenMeth的类型实参.注意,类型实参位于::之后.可以将该语法泛化,当把一个泛型方法指定为方法引用时,他的类型实参就位于::之后且在方法名之前.在指定泛型类的情况下,类型实参位于类名之后且在::之前.