方法引用
通過(guò)前兩個(gè)小節(jié)對(duì)Lambda
表達(dá)式的學(xué)習(xí),本小節(jié)我們來(lái)介紹一個(gè)更加深入的知識(shí)點(diǎn) —— 方法引用。通過(guò)本小節(jié)的學(xué)習(xí),你將了解到什么是方法引用,方法引用的基礎(chǔ)語(yǔ)法,方法引用的使用條件和使用場(chǎng)景,方法引用的分類,方法引用的使用實(shí)例等內(nèi)容。
1. 什么是方法引用
方法引用(Method References
)是一種語(yǔ)法糖,它本質(zhì)上就是 Lambda
表達(dá)式,我們知道Lambda
表達(dá)式是函數(shù)式接口的實(shí)例,所以說(shuō)方法引用也是函數(shù)式接口的實(shí)例。
Tips:什么是語(yǔ)法糖?語(yǔ)法糖(Syntactic sugar),也譯為糖衣語(yǔ)法,是由英國(guó)計(jì)算機(jī)科學(xué)家彼得·約翰·蘭達(dá)(
Peter J. Landin
)發(fā)明的一個(gè)術(shù)語(yǔ),指計(jì)算機(jī)語(yǔ)言中添加的某種語(yǔ)法,這種語(yǔ)法對(duì)語(yǔ)言的功能并沒(méi)有影響,但是更方便程序員使用。通常來(lái)說(shuō)使用語(yǔ)法糖能夠增加程序的可讀性,從而減少程序代碼出錯(cuò)的機(jī)會(huì)。可以將語(yǔ)法糖理解為漢語(yǔ)中的成語(yǔ),用更簡(jiǎn)練的文字表達(dá)較復(fù)雜的含義。在得到廣泛接受的情況下,可以提升交流的效率。
我們來(lái)回顧一個(gè)之前學(xué)過(guò)的實(shí)例:
import java.util.function.Consumer;
public class MethodReferencesDemo1 {
public static void main(String[] args) {
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("只消費(fèi),不返回");
}
}
運(yùn)行結(jié)果:
只消費(fèi),不返回
上例是 Java 內(nèi)置函數(shù)式接口中的消費(fèi)型接口,如果你使用idea
編寫(xiě)代碼,System.out.println(s)
這個(gè)表達(dá)式可以一鍵替換為方法引用,將鼠標(biāo)光標(biāo)放置到語(yǔ)句上,會(huì)彈出提示框,再點(diǎn)擊Replace lambda with method reference
按鈕即可完成一鍵替換:

替換為方法引用后的實(shí)例代碼:
import java.util.function.Consumer;
public class MethodReferencesDemo1 {
public static void main(String[] args) {
Consumer<String> consumer = System.out::println;
consumer.accept("只消費(fèi),不返回");
}
}
運(yùn)行結(jié)果:
只消費(fèi),不返回
我們看到System.out.println(s)
這個(gè)表達(dá)式被替換成了System.out::println
,同樣成功執(zhí)行了代碼,看到這里,同學(xué)們腦袋上可能全是問(wèn)號(hào),這是什么語(yǔ)法?我們之前怎么沒(méi)提過(guò)?別著急,我們馬上就來(lái)講解語(yǔ)法規(guī)則。
2. 語(yǔ)法
方法引用使用一對(duì)冒號(hào)(::
)來(lái)引用方法,格式如下:
類或?qū)ο?:: 方法名
上面實(shí)例中方法引用的代碼為:
System.out::println
其中System.out
就是PrintStream
類的對(duì)象,println
就是方法名。
3. 使用場(chǎng)景和使用條件
方法引用的使用場(chǎng)景為:當(dāng)要傳遞給Lambda
體的操作,已經(jīng)有實(shí)現(xiàn)的方法了,就可以使用方法引用。
方法引用的使用條件為:接口中的抽象方法的形參列表和返回值類型與方法引用的方法形參列表和返回值相同。
4. 方法引用的分類
對(duì)于方法引用的使用,通??梢苑譃橐韵?4 種情況:
對(duì)象 :: 非靜態(tài)方法
:對(duì)象引用非靜態(tài)方法,即實(shí)例方法;類 :: 靜態(tài)方法
:類引用靜態(tài)方法;類 :: 非靜態(tài)方法
:類引用非靜態(tài)方法;類 :: new
:構(gòu)造方法引用。
下面我們根據(jù)以上幾種情況來(lái)看幾個(gè)實(shí)例。
4.1 對(duì)象引用實(shí)例方法
對(duì)象引用實(shí)例方法,我們已經(jīng)在上面介紹過(guò)了,System.out
就是對(duì)象,而println
就是實(shí)例方法,這里不再贅述。
4.2 類引用靜態(tài)方法
類引用靜態(tài)方法,請(qǐng)查看以下實(shí)例:
import java.util.Comparator;
public class MethodReferencesDemo2 {
public static void main(String[] args) {
// 使用 Lambda 表達(dá)式
Comparator<Integer> comparator1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(comparator1.compare(11, 12));
// 使用方法引用,類 :: 靜態(tài)方法( compare() 為靜態(tài)方法)
Comparator<Integer> comparator2 = Integer::compare;
System.out.println(comparator2.compare(12, 11));
}
}
運(yùn)行結(jié)果:
-1
1
查看 Java 源碼,可觀察到compare()
方法是靜態(tài)方法:

我們?cè)賮?lái)看一個(gè)實(shí)例:
import java.util.Comparator;
import java.util.function.Function;
public class MethodReferencesDemo3 {
public static void main(String[] args) {
// 使用 Lambda 表達(dá)式
Function<Double, Long> function1 = d -> Math.round(d);
Long apply1 = function1.apply(1.0);
System.out.println(apply1);
// 使用方法引用,類 :: 靜態(tài)方法( round() 為靜態(tài)方法)
Function<Double, Long> function2 = Math::round;
Long apply2 = function2.apply(2.0);
System.out.println(apply2);
}
}
運(yùn)行結(jié)果:
1
2
4.3 類引用實(shí)例方法
類引用實(shí)例方法,比較難以理解,請(qǐng)查看以下實(shí)例:
import java.util.Comparator;
public class MethodReferencesDemo4 {
public static void main(String[] args) {
// 使用 Lambda 表達(dá)式
Comparator<String> comparator1 = (s1, s2) -> s1.compareTo(s2);
int compare1 = comparator1.compare("Hello", "Java");
System.out.println(compare1);
// 使用方法引用,類 :: 實(shí)例方法( compareTo() 為實(shí)例方法)
Comparator<String> comparator2 = String::compareTo;
int compare2 = comparator2.compare("Hello", "Hello");
System.out.println(compare2);
}
}
運(yùn)行結(jié)果:
-2
0
Comparator
接口中的compare(T t1, T t2)
抽象方法,有兩個(gè)參數(shù),但是String
類下的實(shí)例方法compareTo(String anotherString)
只有 1 個(gè)參數(shù),為什么這種情況下還能使用方法引用呢?這屬于一個(gè)特殊情況,當(dāng)函數(shù)式接口中的抽象方法有兩個(gè)參數(shù)時(shí),已實(shí)現(xiàn)方法的第 1 個(gè)參數(shù)作為方法調(diào)用者時(shí),也可以使用方法引用。此時(shí),就可以使用類來(lái)引用實(shí)例方法了(即實(shí)例中的String::compareTo
)。
4.4 類引用構(gòu)造方法
類引用構(gòu)造方法,可以直接使用類名 :: new
,請(qǐng)查看如下實(shí)例:
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferencesDemo5 {
static class Person {
private String name;
public Person() {
System.out.println("無(wú)參數(shù)構(gòu)造方法執(zhí)行了");
}
public Person(String name) {
System.out.println("單參數(shù)構(gòu)造方法執(zhí)行了");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
// 使用 Lambda 表達(dá)式,調(diào)用無(wú)參構(gòu)造方法
Supplier<Person> supplier1 = () -> new Person();
supplier1.get();
// 使用方法引用,引用無(wú)參構(gòu)造方法
Supplier<Person> supplier2 = Person::new;
supplier2.get();
// 使用 Lambda 表達(dá)式,調(diào)用單參構(gòu)造方法
Function<String, Person> function1 = name -> new Person(name);
Person person1 = function1.apply("小慕");
System.out.println(person1.getName());
// 使用方法引用,引用單參構(gòu)造方法
Function<String, Person> function2 = Person::new;
Person person2 = function1.apply("小明");
System.out.println(person2.getName());
}
}
運(yùn)行結(jié)果:
無(wú)參數(shù)構(gòu)造方法執(zhí)行了
無(wú)參數(shù)構(gòu)造方法執(zhí)行了
單參數(shù)構(gòu)造方法執(zhí)行了
小慕
單參數(shù)構(gòu)造方法執(zhí)行了
小明
在實(shí)例中,我們使用了Lambda
表達(dá)式和方法引用兩種方式,分別調(diào)用了靜態(tài)內(nèi)部類Person
的無(wú)參和單參構(gòu)造方法。函數(shù)式接口中的抽象方法的形參列表與構(gòu)造方法的形參列表一致,抽象方法的返回值類型就是構(gòu)造方法所屬的類型。
5. 小結(jié)
通過(guò)本小節(jié)的學(xué)習(xí),我們知道了方法引用是一個(gè)語(yǔ)法糖,它本質(zhì)上還是Lambda
表達(dá)式。方法引用使用一對(duì)冒號(hào)(::
)來(lái)引用方法。要傳遞給Lambda
體的操作,已經(jīng)有實(shí)現(xiàn)的方法了,就可以使用方法引用;想要使用方法引用,就要求接口中的抽象方法的形參列表和返回值類型與方法引用的方法形參列表和返回值相同。方法引用可能較為抽象,希望同學(xué)們課下多加練習(xí)。