方法引用是用來直接訪問類或者實例的已經存在的方法或者構造方法。方法引用提供了一種引用而不執行方法的方式,它須要由兼容的函數式接口構成的目標類型上下文。計算時,方法引用會建立函數式接口的一個實例。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);