方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它须要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会建立函数式接口的一个实例。java
当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接经过方法引用的形式可读性更高一些。方法引用是一种更简洁易懂的Lambda表达式。数组
注意方法引用是一个Lambda表达式,其中方法引用的操做符是双冒号"::"。app
先看一个例子ide
首先定义一个Person类,以下:函数
package methodreferences; import java.time.LocalDate; public class Person { public Person(String name, LocalDate birthday) { this.name = name; this.birthday = birthday; } String name; LocalDate birthday; public LocalDate getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } @Override public String toString() { return this.name; } }
假设咱们有一个Person数组,而且想对它进行排序,这时候,咱们可能会这样写:this
package methodreferences; import java.time.LocalDate; import java.util.Arrays; import java.util.Comparator; public class Main { static class PersonAgeComparator implements Comparator<Person> { public int compare(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } } public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("003", LocalDate.of(2016,9,1)), new Person("001", LocalDate.of(2016,2,1)), new Person("002", LocalDate.of(2016,3,1)), new Person("004", LocalDate.of(2016,12,1))}; Arrays.sort(pArr, new PersonAgeComparator()); System.out.println(Arrays.asList(pArr)); } }
其中,Arrays类的sort方法定义以下:spa
public static <T> void sort(T[] a, Comparator<? super T> c)
这里,咱们首先要注意Comparator
接口是一个函数式接口,所以咱们可使用Lambda表达式,而不须要定义一个实现Comparator
接口的类,并建立它的实例对象,传给sort方法。指针
使用Lambda表达式,咱们能够这样写:code
package methodreferences; import java.time.LocalDate; import java.util.Arrays; public class Main { public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("003", LocalDate.of(2016,9,1)), new Person("001", LocalDate.of(2016,2,1)), new Person("002", LocalDate.of(2016,3,1)), new Person("004", LocalDate.of(2016,12,1))}; Arrays.sort(pArr, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); }); System.out.println(Arrays.asList(pArr)); } }
然而,在以上代码中,关于两我的生日的比较方法在Person类中已经定义了,所以,咱们能够直接使用已存在的Person.compareByAge方法。对象
package methodreferences; import java.time.LocalDate; import java.util.Arrays; public class Main { public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("003", LocalDate.of(2016,9,1)), new Person("001", LocalDate.of(2016,2,1)), new Person("002", LocalDate.of(2016,3,1)), new Person("004", LocalDate.of(2016,12,1))}; Arrays.sort(pArr, (a, b) -> Person.compareByAge(a, b)); System.out.println(Arrays.asList(pArr)); } }
由于这个Lambda表达式调用了一个已存在的方法,所以,咱们能够直接使用方法引用来替代这个Lambda表达式,
package methodreferences; import java.time.LocalDate; import java.util.Arrays; public class Main { public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("003", LocalDate.of(2016,9,1)), new Person("001", LocalDate.of(2016,2,1)), new Person("002", LocalDate.of(2016,3,1)), new Person("004", LocalDate.of(2016,12,1))}; Arrays.sort(pArr, Person::compareByAge); System.out.println(Arrays.asList(pArr)); } }
在以上代码中,方法引用Person::compareByAge在语义上与Lambda表达式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有以下特性:
组成语法格式:ClassName::staticMethodName
注意:
例子:
String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y);
字符串反转的例子:
/* * 函数式接口 * */ interface StringFunc { String func(String n); } class MyStringOps { //静态方法: 反转字符串 public static String strReverse(String str) { String result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; } } class MethodRefDemo { public static String stringOp(StringFunc sf, String s) { return sf.func(s); } public static void main(String[] args) { String inStr = "lambda add power to Java"; //MyStringOps::strReverse 至关于实现了接口方法func() // 并在接口方法func()中做了MyStringOps.strReverse()操做 String outStr = stringOp(MyStringOps::strReverse, inStr); System.out.println("Original string: " + inStr); System.out.println("String reserved: " + outStr); } } 输出结果: Original string: lambda add power to Java String reserved: avaJ ot rewop dda adbmal
表达式MyStringOps::strReverse的计算结果为对象引用,其中,strReverse提供了StringFunc的func()方法的实现。
找到列表中具备最大值的对象
class MyClass { private int val; MyClass(int v) { val = v; } public int getValue() { return val; } } class UseMethodRef { public static int compareMC(MyClass a, MyClass b) { return a.getValue() - b.getValue(); } public static void main(String[] args) { ArrayList<MyClass> a1 = new ArrayList<MyClass>(); a1.add(new MyClass(1)); a1.add(new MyClass(4)); a1.add(new MyClass(2)); a1.add(new MyClass(9)); a1.add(new MyClass(3)); a1.add(new MyClass(7)); //UseMethodRef::compareMC生成了抽象接口Comparator定义的compare()方法的实例。 MyClass maxValObj = Collections.max(a1, UseMethodRef::compareMC); System.out.println("Maximum value is: " + maxValObj.getValue()); } } 输出结果: Maximum value is: 9
UseMethodRef定义了静态方法compareMC(),它与Comparator定义的compare()方法兼容。所以,没哟必要显式的实现Comparator接口并建立其实例。
这种语法与用于静态方法的语法相似,只不过这里使用对象引用而不是类名。
实例方法引用又分如下三种类型
组成语法格式:instanceReference::methodName
例子:
Function<String, String> upper = String::toUpperCase;
反转字符串
/* * 函数式接口 * */ interface StringFunc { String func(String n); } class MyStringOps { //普通方法: 反转字符串 public String strReverse(String str) { String result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; } } class MethodRefDemo2 { public static String stringOp(StringFunc sf, String s) { return sf.func(s); } public static void main(String[] args) { String inStr = "lambda add power to Java"; MyStringOps strOps = new MyStringOps();//实例对象 //MyStringOps::strReverse 至关于实现了接口方法func() //并在接口方法func()中做了MyStringOps.strReverse()操做 String outStr = stringOp(strOps::strReverse, inStr); System.out.println("Original string: " + inStr); System.out.println("String reserved: " + outStr); } } 输出结果: Original string: lambda add power to Java String reserved: avaJ ot rewop dda adbmal
这里使用了类的名称,而不是具体的对象,尽管指定的是实例方法。使用这种形式时,函数式接口的第一个参数匹配调用对象,第二个参数匹配方法指定的参数。
组成语法格式:super::methodName
方法的名称由methodName指定
经过使用super,能够引用方法的超类版本。
例子:
还能够捕获this 指针
this :: equals 等价于lambda表达式 x -> this.equals(x);
组成语法格式:ClassName::methodName
注意:
若类型的实例方法是泛型的,就须要在::分隔符前提供类型参数,或者(多数状况下)利用目标类型推导出其类型。
静态方法引用和类型上的实例方法引用拥有同样的语法。编译器会根据实际状况作出决定。
通常咱们不须要指定方法引用中的参数类型,由于编译器每每能够推导出结果,但若是须要咱们也能够显式在::分隔符以前提供参数类型信息。
例子:
String::toString 等价于lambda表达式 (s) -> s.toString()
这里不太容易理解,实例方法要经过对象来调用,方法引用对应Lambda,Lambda的第一个参数会成为调用实例方法的对象。
在泛型类或泛型方法中,也可使用方法引用。
interface MyFunc<T> { int func(T[] als, T v); } class MyArrayOps { public 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 { public 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(MyArrayOps::<Integer>countMatching, vals, 4); System.out.println("vals contains "+count+" 4s"); count=myOp(MyArrayOps::<String>countMatching, strs, "Two"); System.out.println("strs contains "+count+" Twos"); } } 输出结果: vals contains 3 4s strs contains 2 Twos
分析:
在程序中,MyArrayOps是非泛型类,包含泛型方法countMatching()。该方法返回数组中与指定值匹配的元素的个数。注意这里如何指定泛型类型参数。例如,在main()方法中,对countMatching()方法的第一次调用以下所示:count = myOp(MyArrayOps::<Integer>countMatching,vals,4);
这里传递了类型参数Integer。
注意,参数传递发生在::的后面。这种语法能够推广。当把泛型方法指定为方法引用时,类型参数出如今::以后、方法名以前。可是,须要指出的是,在这种状况(和其它许多状况)下,并不是必须显示指定类型参数,由于类型参数会被自动推断得出。对于指定泛型类的状况,类型参数位于类名的后面::的前面。
构造方法引用又分构造方法引用和数组构造方法引用。
组成语法格式:Class::new
构造函数本质上是静态方法,只是方法名字比较特殊,使用的是new 关键字。
例子:
String::new, 等价于lambda表达式 () -> new String()
interface MyFunc { MyClass func(int n); } class MyClass { private int val; MyClass(int v) { val = v; } MyClass() { val = 0; } public int getValue() { return val; } } class ConstructorRefDemo { public static void main(String[] args) { MyFunc myClassCons = MyClass::new; MyClass mc = myClassCons.func(100); System.out.println("val in mc is: " + mc.getValue()); } } 输出结果: val in mc is: 100
组成语法格式:TypeName[]::new
例子:
int[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度。
等价于lambda表达式 x -> new int[x]。
假想存在一个接收int参数的数组构造方法
IntFunction<int[]> arrayMaker = int[]::new;
int[] array = arrayMaker.apply(10) // 建立数组 int[10]
以下示例,这里引用的是字符串数组中任意一个对象的compareToIgnoreCase方法。
String[] stringArray = { "Barbara", "James", "Mary", "Linda" }; Arrays.sort(stringArray, String::compareToIgnoreCase);