之前的文章已經介紹了函數式介面與lambda運算式,這篇文章主要學習下方法引用。使用方法引用,可以減少lambda運算式的書寫,在Stream API中很常用。
一段代碼對比下lambda與靜態方法引用:
List<Integer> ids = Arrays.asList(1, 2, 5, 4, 3);// 使用lambda運算式Comparator<Integer> comparator1 = (a, b) -> a - b;// 使用靜態方法引用Comparator<Integer> comparator2 = Integer::compare;List<Integer> sorted1 = ids.stream().sorted(comparator1).collect(Collectors.toList());List<Integer> sorted2 = ids.stream().sorted(comparator2).collect(Collectors.toList());System.out.println(ids);// [1, 2, 5, 4, 3]System.out.println(sorted1); // [1, 2, 3, 4, 5]System.out.println(sorted2); // [1, 2, 3, 4, 5]
我們想實現整數list的排序,使用lambda我們還得自己編寫一個Comparator對象(雖然也很簡單),實際上JDK類庫已經提供了類似的實現,我們通過Integer::compare就可以引用已經存在的方法。
JDK8中的方法引用分成4類:靜態方法引用、執行個體方法引用、構造方法引用、以靜態方式引用執行個體方法。
(1).靜態方法引用
格式:ClassName::staticMethodName,比如:
String::valueOf 等價於lambda運算式 (s) -> String.valueOf(s)
Math::pow 等價於lambda運算式 (x, y) -> Math.pow(x, y)
@FunctionalInterfacepublic interface MyInterface { public double calculate(double a, double b);}
// 靜態方法引用powMyInterface ins1 = Math::pow;System.out.println(ins1.calculate(2, 4) == 16);// 靜態方法引用maxMyInterface ins2 = Math::max;System.out.println(ins2.calculate(2, 4) == 4);
可以看到我們定義的函數式介面MyInterface,必須要與其引用的靜態方法,具有同樣的傳回值和入參。
(2).執行個體方法引用
格式:instanceReference::methodName,比如:
str::toString 等價於lambda運算式 () -> str.toString()
str::concat 等價於lambda運算式 (another) -> str.concat(another)
@FunctionalInterfacepublic interface MyInterface { // 與String.concat()同樣的入參和傳回值 public String transform(String input);}
String content = "abc";// 引用String類的執行個體方法MyInterface ins1 = content::concat;System.out.println(ins1.transform("def"));// abcdef 可以看到我們實現的效果:定義一個函數式介面,它的方法與原始的concat擁有同樣的參數類型和傳回值,相當於是給concat重新命名。
(3).構造方法引用
格式:ClassName::new,如果ClassName有多個建構函式,那麼JDK會自動根據函數式介面的方法聲明來決定到底使用哪兒一個建構函式。
public class Target { public int attr = 0; public Target() { } public Target(int b) { this.attr = b; }}
@FunctionalInterfacepublic interface MyInterface { public Target create(int value);}
MyInterface ins = Target::new;Target t = ins.create(1); // 可以自動推斷使用的建構函式System.out.println(t.attr);// 1
數組的建構函式與之類似,不過是建構函式有個參數(數組長度)。
@FunctionalInterfacepublic interface MyInterface { public int[] create(int length);}
MyInterface ins = int[]::new;int[] array = ins.create(10);System.out.println(array.length);// 10
(4).以靜態方式引用執行個體方法
不知道叫什麼名字才合適,姑且這麼叫吧。靜態方法引用和類型上的執行個體方法引用擁有一樣的文法,編譯器會根據實際情況做出決定。
格式:ClassName::instanceMethod,比如:
String::toString 等價於lambda運算式 (String s) -> s.toString()
Lambda的第一個參數會成為調用執行個體方法的對象。
public class Target { private int attr = 0; public Target(int attr) { this.attr = attr; } public int compareTo(Target another) { return this.attr - another.attr; } public static int compare(Target one, Target another) { return one.attr - another.attr; }}
// 函數式介面:實現Target對象比較@FunctionalInterfacepublic interface MyInterface { public int compare(Target one, Target another);}
Target target1 = new Target(10);Target target2 = new Target(100);// 引用執行個體方法MyInterface ins1 = Target::compare;System.out.println(ins1.compare(target1, target2));// -90// 引用靜態方法MyInterface ins2 = Target::compare;System.out.println(ins2.compare(target1, target2));// -90
最後給個例子,看下方法引用的威力:
public class Person { private String name; private Date birthday; public Person(String name, Date birthday) { this.name = name; this.birthday = birthday; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'; }}
List<Person> persons = new ArrayList<>();persons.add(new Person("c", "2017-01-01"));persons.add(new Person("b", "2016-01-01"));persons.add(new Person("a", "2015-01-01"));
現在我們想對persons集合按照name排序:
// 方式1Collections.sort(persons, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getName().compareTo(o2.getName());}});System.out.println(persons); 方式一是比較傳統的做法,自己實現一個Comparator比較子。
// 方式2Collections.sort(persons, (o1, o2)->o1.getName().compareTo(o2.getName()));System.out.println(persons);
方式2使用了lambda運算式
// 方式3Collections.sort(persons, Comparator.comparing(Person::getName));System.out.println(persons);
方式3使用了方法引用,可以複用比較邏輯,不用自己實現Comparator。
參考文章:https://my.oschina.net/luoyezhuifeng/blog/801343