毫無(wú)疑問(wèn)沉迹,Java 8是Java自Java 5(發(fā)布于2004年)之后的最重要的版本。這個(gè)版本包含語(yǔ)言开睡、編譯器因苹、庫(kù)较店、工具和JVM等方面的十多個(gè)新特性。
在本文中我們將學(xué)習(xí)這些新特性其中之一,函數(shù)編程的lambda語(yǔ)法,為什么要學(xué)學(xué)習(xí)lambda,簡(jiǎn)而言之即使為了代碼更加簡(jiǎn)潔,表達(dá)思想更加清晰,更有利于代碼的維護(hù).
我們先簡(jiǎn)單的看一個(gè)入門(mén)案例:
@Test
public void test() {
//啟動(dòng)一個(gè)線(xiàn)程,輸出一句話(huà)
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名內(nèi)部類(lèi),創(chuàng)建一個(gè)線(xiàn)程打印一句話(huà)");
}
});
}
@Test
public void testLambdaTest() {
//使用lambda,啟動(dòng)一個(gè)線(xiàn)程,輸出一句話(huà)
new Thread(()-> System.out.println("使用lambda打印一句話(huà)."));
}
是否感受到區(qū)別?如果還不明顯我們?cè)倥e一個(gè)栗子.
又一個(gè)栗子
@Setter@Getter
public class Student {
//學(xué)號(hào)
private Integer number;
//年齡
private Integer age;
//身高
private Integer height;
public Student(Integer number, Integer age, Integer height) {
this.number = number;
this.age = age;
this.height = height;
}
}
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,17,170));
students.add(new Student(2,18,171));
students.add(new Student(3,18,182));
//現(xiàn)在我有一個(gè)需求,找出學(xué)生中年齡滿(mǎn)18歲,且身高在180以下的,按學(xué)號(hào)排序
//傳統(tǒng)方式
ArrayList<Student> result = new ArrayList<>();
for (Student student : students) {
if (student.getAge().equals(18) && student.getHeight() < 180) {
result.add(student);
}
}
Collections.sort(result, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getNumber() - o2.getNumber();
}
});
//lambada
students.stream().filter(c -> (c.getAge().equals(18) && c.getHeight() < 180)).sorted(Comparator.comparing(Student::getNumber);
傳統(tǒng)方式如果熟練的業(yè)務(wù)的老程序員,當(dāng)然可以很快些出來(lái),可是如果是維護(hù)的別人的代碼的邏輯再稍微復(fù)雜一點(diǎn),地起來(lái)
是不是有點(diǎn)費(fèi)勁,且有些語(yǔ)義不清,看了第一行代碼還不知道在干嘛.而lambada的方式是不是很直觀的知道別人的 業(yè)務(wù)邏輯.
一個(gè)小細(xì)節(jié):
sort(Student::getNumber),這個(gè)寫(xiě)法是錯(cuò)誤的.Java8 Stream()引發(fā)的“non-static method cannot be referenced from a static context”.會(huì)引發(fā)這個(gè)錯(cuò)誤.具體見(jiàn):http://www.reibang.com/p/3db8bf90601d
lambda的語(yǔ)法結(jié)構(gòu): 參數(shù)列表 -> 表達(dá)式/方法體
“->”被稱(chēng)為L(zhǎng)ambda 操作符或箭頭操作符容燕。
它將Lambda 分為兩個(gè)部分:
左側(cè):指定了Lambda 表達(dá)式需要的所有參數(shù)
右側(cè):指定了Lambda 體梁呈,即Lambda 表達(dá)式要執(zhí)行的任務(wù)、功能或者說(shuō)是行為蘸秘。
通過(guò)分析lambda表達(dá)式,總結(jié):
Lambda表達(dá)式極大的豐富了Java語(yǔ)言的表現(xiàn)力,簡(jiǎn)化了Java代碼的編寫(xiě),可推斷則可省略官卡!能省則省.
可以寫(xiě)得更少,做的更多.
參數(shù)列表,是需要覆蓋的抽象方法的參數(shù)列表.
- 如果沒(méi)有參數(shù),直接使用(), 這一對(duì)()不能省略
- 如果只有一個(gè)參數(shù),并且參數(shù)寫(xiě)了類(lèi)型,那么必須要使用()
- 如果只有一個(gè)參數(shù),并且參數(shù)沒(méi)寫(xiě)類(lèi)型,那么可以省略()
- 如果有兩個(gè)或多個(gè)參數(shù),不管參數(shù)是否寫(xiě)了類(lèi)型,都要使用()
- 如果參數(shù)要加上修飾符或者注解(比如final),參數(shù)一定要寫(xiě)類(lèi)型,并且要使用()
如果編譯器可以推導(dǎo),那么我們就可省略.
注意:
在某些情況下,類(lèi)型信息可以幫助閱讀者理解代碼醋虏,這時(shí)需要手動(dòng)聲明類(lèi)型寻咒,讓代碼便于閱讀。有時(shí)省略類(lèi)型信息更加簡(jiǎn)潔明了颈嚼,還可以減少干擾毛秘,讓閱讀者關(guān)注業(yè)務(wù)邏輯,這時(shí)就可以省略阻课;所以在開(kāi)發(fā)中建議大家根據(jù)自己或項(xiàng)目組的習(xí)慣結(jié)合實(shí)際情況叫挟,進(jìn)行類(lèi)型聲明或省略。
函數(shù)接口
是不是所有使用到接口類(lèi)型的參數(shù)的地方,都可以使用Lambda表達(dá)式?
能省則省.想要讓編譯器能夠推導(dǎo)出來(lái)到底是調(diào)用的那個(gè)方法,那么說(shuō)明,能夠簡(jiǎn)寫(xiě)的接口,必須只能有一個(gè)抽象方法.如果有多個(gè),編譯器是不知道到底要推導(dǎo)成哪個(gè)方法.
思考一個(gè)問(wèn)題:是不是所有的接口類(lèi)型作為參數(shù)傳遞(傳統(tǒng)寫(xiě)法就是匿名內(nèi)部類(lèi))都可以使用Lambda表達(dá)式呢限煞?
只要確保接口中有且僅有一個(gè)抽象方法(不包括Object繼承過(guò)來(lái)的方法)即可.只有函數(shù)式接口才可以使用lambda表達(dá)式來(lái)進(jìn)行函數(shù)式編程.
格式:
修飾符 interface 接口名稱(chēng) {
[public abstract] 返回值類(lèi)型 方法名稱(chēng)(可選參數(shù)信息);
// 其他
}
為了便于區(qū)分并便于編譯器進(jìn)行語(yǔ)法校驗(yàn),JDK8中還引入了一個(gè)新的注解:
@FunctionalInterface
該注解可用于一個(gè)接口的定義上,一旦使用該注解來(lái)定義接口抹恳,編譯器將會(huì)強(qiáng)制檢查該接口是否確實(shí)有且僅有一個(gè)抽象方法,否則將會(huì)報(bào)錯(cuò)署驻。
需要注意的是奋献,即使不使用該注解,只要滿(mǎn)足函數(shù)式接口的定義旺上,該接口仍然是一個(gè)函數(shù)式接口瓶蚂,使用起來(lái)都一樣。(可以類(lèi)比下@Override 注解的作用)
我們可以去看下Runnable接口的源碼,發(fā)現(xiàn)它就是一個(gè)函數(shù)式接口
那么我們也可以來(lái)定義一個(gè)簡(jiǎn)單的函數(shù)式接口:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
lambda表達(dá)式存在的主要意義是:簡(jiǎn)化函數(shù)式接口的使用.
注意,函數(shù)式接口,只能在使用的時(shí)候,編譯器推導(dǎo)出來(lái).