方法引用

Java8中有一个重要的特性与lambda表达式相关,叫做方法引用。方法引用提供了一种引用而不是执行方法的方式。这种特性与lambda表达式相关,因为它也需要由兼容的函数式接口构成的目标类上下文。计算时,方法引用也会创建函数式接口的一个实例。

1. 静态方法的引用

要创建静态方法引用,需要使用下面的一般语法:
className::methodName
注意,类名与方法名之间用双冒号分开。::是jdk8新增的一个分隔符,专门用于此目的。在与目标类型兼容的任何地方,都可以使用这个方法引用。
示例:

interface StringFunc{
    String func(String s);
}
class MyStringOps{
	//A static method that reverse a string
    static String reverse(String str)
    {
        String result = "";
        for(int i = str.length()-1; i>=0; i--)
            result += str.charAt(i);
        return result;
    }
}

class MyClass{
	static String stringOp(StringFunc sf, String s)
	{
	    return sf.func(s);
	}
	public static void main(String[] args)
	{
		String inStr = "lambda add power to java";
		String outStr = stringOp(MyStringOps::reverse,inStr);
		System.out.println("string reversed:"+outStr);
	}
}

在程序中,特别注意下面这行代码:
String outStr = stringOp(MyStringOps::strReverse, inStr);
这里将对MyStringOps内声明静态方法strReverse()的引用传递给了stringOp()方法的第一个参数,可以这么做,因为strReverse与StringFunc函数式接口兼容。strReverse提供了StringFunc的func()方法的实现

2. 实例方法的方法引用

(1) 要传递对某个对象的实例的引用,需要下面的基本语法:
objRef::methodName
上例中,将strReverse()方法声明为一个实例方法。在main方法里创建一个MyStringOps的一个实例strOps,在调用stringOp时,这个实例用于创建对strReverse的方法引用,如下所示:

String outStr = stringOp(strOps::strReverse, inStr);

此时,对strOps对象调用strReverse()方法。
(2) 也可以指定一个实例方法,使其能用于给定类的 任何对象而不仅指定对象。此时,需要像下面这样创建方法引用:
className::instanceMethodName
这里使用了类的名称,而不是具体的对象,尽管指定的是实例方法。使用这种形式时,函数式接口的第一个参数匹配调用对象,第二个参数匹配方法指定的参数。下面的例子中,定义了一个方法counter(),用于统计某个数组中满足函数式接口MyFunc的func()方法定义的条件的对象个数。

interface MyFunc<T>{
	boolean func(T v1, T v2);
}
class HighTemp{
	private int hTemp;
	HighTemp(int ht){hTemp = ht;}
	boolean sameTemp(HighTemp ht2)
	{
	    return hTemp == ht2.hTemp;
    }
    boolean lessThanTemp(HighTemp ht2)
    {
		return hTemp < ht2.hTemp;
	}
}
class InstanceMethodWithObjectRefDemo{
	static<T> int counter(T[] vals, MyFunc<T> f, T v)
	{
		int count = 0; 
		for(int i = 0; i < vals.length; i++)
		{
			if(f.func(vals[i], v))
				count++;
		}
		return count;
	}
	public static void main(String[] args)
	{
		int count = 0;
		//create an array of HighTemp objects
		HighTemp[] weekDayHighs = { new HighTemp(89), new HighTemp(82), new HighTemp(90), new HighTemp(89), new HighTemp(91), new HighTemp(83)};
		count = counter(weekDayHighs, HighTemp::sameTemp, new HighTemp(89));
		System.out.println(count+" days had a high of 89");
		count = counter(weekDayHighs, HighTemp::lessThanTemp, new HighTemp(90));
		System.out.println(count+" days had a high of less than 90");
	}
}

sameTemp()方法和lessThanTemp()方法都有一个HightTemp类型的参数,并且都返回布尔类型结果。因此,这两个方法都与MyFunc函数式接口兼容,因为调用对象类型可以映射到func()方法的第一个参数。传递的实参可以映射到func()方法的第二个参数。因此下面的表达式HighTemp::sameTemp被传递给counter时,会创建MyFunc的一个实例,其中第一个参数的参数类型就是实例方法的调用对象的类型,也就是HighTemp。第二个参数的类型也是HighTemp,因为这是sampTemp()方法的参数。对于lessThanTemp(),这也是成立的。
另外,通过使用super,可以引用方法的超类版本。如下所示:
super::methodName

关于实例方法的方法引用的两种方式,下面的例子可以说明一些事情:

@FunctionalInterface
interface TheInterface
{
    int myMethod(String str, int index);
}

public class Test02 {

    public static void main(String[] args) {
        TheInterface theInterface = String::indexOf;
        //第一参数指的是调用indexOf方法的实例,indexOf(int ch)
        int index = theInterface.myMethod("hello world",'o');
        System.out.println(index);
        
        theInterface = "hello world"::indexOf;
        //隐含的第一个参数this,indexOf(String str, int fromIndex)
        index = theInterface.myMethod("o", 1);
        System.out.println(index);
    }
}

3. 在泛型类或泛型方法中,也可以使用方法引用

interface MyFunc<T>{
	int func(T[] vals, T v);
} 
class MyArrayOps{
	static<T> int countMatching(T[] vals, T v){
		int count = 0;
		for(int i = 0; i < vals.length;i++)
			if(vals[i] == v) count++;
		return count;
	}
}
class GenericMethodRefDemo{
	static<T> int myOp(MyFunc<T> f, T[] vals, T v)
	{
		return f.func(vals, v);
	}
	public static void main(String args[])
	{
		Integer[] vals = {1,2,3,4,2,3,4,4,5};
		String[] strs = {"one", "two", "three", "two"};
		int count;
		count = myOp(MyArray::<Integer>countMatching, vals, 4);
		System.out.println("vals contains" + count + " 4s");
		count = myOp(MyArray::<String>countMatching, strs, "two");
		System.out.println("strs contain" + count + "twos");
	}
} 

在程序中,MyArrayOps是非泛型类,包含泛型方法countMatching()。该方法返回数组中与制定值匹配的元素个数。注意这里如何制定泛型类型参数。需要指出的是,这种情况(和其他许多情况)下,并非必须显示地指定类型参数,因为类型参数会被自动推断得出。对于指定泛型类的情况,类型参数位于类名的后面,::号的前面。

4. 一展拳脚

上面的例子显示了使用方法引用的机制,但没有展现他们的真正优势。方法引用能够一展拳脚的地方是在与集合框架一起使用的时。
找到集合中最大元素的一种方法是使用Collections类定义的max()方法。对于这里使用的max()方法,必须传递一个集合引用,以及一个Comparator< T >接口的一个实例作为比较器。在jdk8中,现在可以简单地将比较方法的引用传递给max()方法,因为这将自动实现比较器。

class MyClass{
	private int val;
	MyClass(int v){ val = v;}
	int getVal() { return val;}
}
class UseMethodRef{
	static int compareMC(MyClass a, MyClass b)
	{
		return a.getVal() - b.getVal();
	}
	public static void main(String args[])
	{
		ArrayList<MyClass> al = new ArrayList<MyClass>();
		al.add(new MyClass(1));
		al.add(new MyClass(4));
		al.add(new MyClass(2));
		al.add(new MyClass(9));
		MyClass maxValObj = Collecitons.max(al, UseMethodRef::compareMC);
		System.out.println("maximun value is:" + maxValObj.getVal());
	}`
}

注意:MyClass没有定义自己的比较器,也没有实现Comparator接口,但仍可以通过max()方法获得了MyClass对象对象列表中的最大值。这是因为UseMethodRef定义了静态方法compareMC(),它与Comparator定义的compare()方法兼容。因此,没有必要现实地实现Comparator接口并创建其实例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值