您的位置:首页 > 其它

方法引用

2015-12-31 20:35 211 查看
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
HightTemp[] 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.outl.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

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接口并创建其实例。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: