方法引用

有一个重要的特性与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的类型实参.注意,类型实参位于::之后.可以将该语法泛化,当把一个泛型方法指定为方法引用时,他的类型实参就位于::之后且在方法名之前.在指定泛型类的情况下,类型实参位于类名之后且在::之前.