我的御姐兒?jiǎn)眩揪邮慨?dāng)初答應(yīng)你的話,現(xiàn)在開(kāi)始履行了。
一、什么是函數(shù)式編程
函數(shù)式編程的核心思想是:思考問(wèn)題時(shí),使用不可變值和函數(shù),函數(shù)對(duì)一個(gè)值進(jìn)行處理,映射成另一個(gè)值。類似于數(shù)學(xué)中的函數(shù)求值。
我們對(duì)這種思想的完全定義還為時(shí)尚早。我們現(xiàn)在想要的是如何通過(guò)使用函數(shù)式編程風(fēng)格的代碼來(lái)寫(xiě)出好代碼。
本系列將講述如何實(shí)用的使用函數(shù)式編程。幫助程序媛們(恩,沒(méi)打錯(cuò)!!!)能寫(xiě)出易讀、易維護(hù)的代碼。
下面開(kāi)始進(jìn)入正題。
二、Lambda表達(dá)式
2.1 在Android studio中使用Java8 的Lambda表達(dá)式的配置
首先,當(dāng)然需要有1.8.0以上的jdk嘍,所以別忘記了。
其次,對(duì)于要使用Java8 的Lambda表達(dá)式的項(xiàng)目,對(duì)app的bulid.gradle進(jìn)行配置。
apply plugin: 'me.tatarka.retrolambda'
//設(shè)置JDK1.8
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
最后,對(duì)于要使用Java8 的Lambda表達(dá)式的項(xiàng)目,在項(xiàng)目的bulid.gradle的buildscript里dependencies里進(jìn)行配置。
classpath 'me.tatarka:gradle-retrolambda:3.2.5'
接下來(lái)我們就可以正常的使用Java8 的Lambda表達(dá)式了。
2.2 初步了解Lambda表達(dá)式
我們先來(lái)看一個(gè)簡(jiǎn)單的按鈕的單擊事件,為了響應(yīng)用戶的操作,通過(guò)注冊(cè)一個(gè)事件監(jiān)聽(tīng)器。用戶一旦點(diǎn)擊按鈕,監(jiān)聽(tīng)器就會(huì)執(zhí)行相應(yīng)的操作。
btn.setOnClickListener(new Button.OnClickListener(){//創(chuàng)建監(jiān)聽(tīng)
public void onClick(View view) {
System.out.println("仁昌居士") ;
}
});
這個(gè)例子中,創(chuàng)建了一個(gè)新對(duì)象,實(shí)現(xiàn)了Button.OnClickListener接口。這個(gè)接口只有一個(gè)方法onClick(View v),當(dāng)用戶點(diǎn)擊按鈕時(shí),按鈕btn就會(huì)調(diào)用這個(gè)方法。匿名內(nèi)部類實(shí)現(xiàn)了該方法。輸出一條“仁昌居士” 的信息,表示按鈕已被點(diǎn)擊過(guò)。
使用匿名內(nèi)部類就是為了方便程序媛將代碼左邊一個(gè)代表某種行為的對(duì)象以數(shù)據(jù)的形式進(jìn)行傳遞。不過(guò),一眼所見(jiàn),匿名內(nèi)部類太冗雜了。我有用的邏輯代碼就是
System.out.println("仁昌居士") ;
但我不得不再加上一堆樣板代碼。
為了更簡(jiǎn)單,更容易的表達(dá)程序媛們的想法,在Java 8中,就引入了Lambda表達(dá)式,這種緊湊的、傳遞行為而不是對(duì)象的方式。
可將上述匿名類的使用簡(jiǎn)化為:
btn.setOnClickListener( view ->System.out.println("仁昌居士"));
分析這段代碼,能發(fā)現(xiàn),和傳入一個(gè)實(shí)現(xiàn)OnClickListener接口的對(duì)象不同,我們傳入的僅僅是一個(gè)函數(shù),view是參數(shù)名,和上面匿名內(nèi)部類示例中的是同一個(gè)參數(shù)。“->”就當(dāng)做指示符好吧。主題是后面的用戶點(diǎn)擊button時(shí)會(huì)運(yùn)行的一些代碼。
我們能發(fā)現(xiàn),在Lambda表達(dá)式中 view并沒(méi)有像在匿名內(nèi)部類里一樣顯式的聲明參數(shù)類型 View view,這是因?yàn)椋琷avac能夠根據(jù)程序的上下文(OnClickListener方法的簽名)在后臺(tái)推斷出參數(shù)view的類型,從而可以正常的編譯。所以對(duì)于明顯的參數(shù)類型不需要顯示聲明。**但是!**是不是就不能顯式聲明了呢?不,也可以顯示聲明的。應(yīng)為,Java 8 是靜態(tài)語(yǔ)言,編譯器有時(shí)候不能根據(jù)上下文推斷出參數(shù)的類型。所以該加的時(shí)候就加。對(duì)我個(gè)人而言,我還是喜歡顯式聲明,因?yàn)槲沂遣锁B(niǎo)嘛。
2.3 Lambda表達(dá)式的幾種表現(xiàn)形式
Lambda表達(dá)式除了基本的形式外,還有幾種不同的寫(xiě)法。
先上代碼:
public class MainActivity extends AppCompatActivity {
private OnTestListener onTestListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.btn);
button.setOnClickListener(view -> System.out.println("仁昌居士"));
this.setOnTestListener(x, y -> System.out.println("仁昌居士"));
Runnable runnable = () -> System.out.println("仁昌居士");
Runnable runnable12 = () -> {
System.out.println("仁昌居士");
};
Button.OnClickListener clickListener = view -> System.out.println("仁昌居士");
BinaryOperator<Integer> integer = (x, y) -> x + y;
BinaryOperator<Integer> integer2 = (Integer x, Integer y) -> x + y;
Integer integer3 = (Integer x, Integer y) -> x + y;
}
public interface OnTestListener {
void onTestClick(int x, int y);
}
public OnTestListener getOnTestListener() {
return onTestListener;
}
public void setOnTestListener(OnTestListener onTestListener) {
this.onTestListener = onTestListener;
}
}
再截圖分析:
1和2,是為了另一個(gè)注意點(diǎn),我后面會(huì)提到。
先看3,3中所示Lambda表達(dá)式實(shí)現(xiàn)了Runnable接口,該接口只有一個(gè)run方法,沒(méi)有參數(shù),返回類型為void ,寫(xiě)法上就使用"()",不包含參數(shù)。
再看4,4中所示Lambda表達(dá)式參數(shù)和3中一樣,區(qū)別在于贏了“{}”,這是用于里面的代碼是代碼塊的時(shí)候,也可以通過(guò)return和跑出異常半路退出。
接著看5,5中所示Lambda表達(dá)式僅僅只有一個(gè)參數(shù),這個(gè)時(shí)候可以選擇省略參數(shù)外的括號(hào)。
再者看6,6中所示Lambda表達(dá)式的參數(shù)是多個(gè)參數(shù)。這個(gè)時(shí)候要注意了:這行“x+y”并不是一個(gè)相加后的結(jié)果,而是一個(gè)函數(shù)操作的過(guò)程本身。
此外,可以發(fā)現(xiàn)6的這兩個(gè)參數(shù)并沒(méi)有聲明類型,那是因?yàn)長(zhǎng)ambda表達(dá)式中的參數(shù)類型都是由編譯器推斷得出的。雖然簡(jiǎn)單了,但是最好顯示聲明參數(shù)類型,如7。
接著我就要提到1,2了??戳耍?,7后發(fā)現(xiàn)賦值的類型的是BinaryOperator<T>。我如果寫(xiě)成8,則會(huì)報(bào)錯(cuò),查看原因,顯示賦值的的類型要是個(gè)接口。
查看BinaryOperator,能夠發(fā)現(xiàn)他是個(gè)interface。
所以回看1,2,為啥我可以這么寫(xiě),因?yàn)槲彝ㄟ^(guò)Lambda表達(dá)式得到的是個(gè)接口。
2.4 Lambda表達(dá)式引用的是值而不是變量
使用匿名內(nèi)部類的時(shí)候,也許會(huì)引用他所在方法里的變量,這個(gè)時(shí)候,需要將變量聲明為final。這就意味著無(wú)法再重復(fù)為其賦值,賦給該變量的是一個(gè)特定的值。
所以寫(xiě)法該是:
注意,引用的變量是final。有時(shí)候你不寫(xiě)出來(lái)也不會(huì)提示報(bào)錯(cuò),是因?yàn)槟J(rèn)給你加了final了。
Lambda表達(dá)式中也無(wú)法用非終態(tài)變量,如果使用,編譯器也會(huì)報(bào)錯(cuò)的。所以,Lambda表達(dá)式引用的其實(shí)是該變量第一次賦值的值,而不是變量。
綜上所述,可以對(duì)Lambda表達(dá)式有一個(gè)概念:Lambda表達(dá)式是靜態(tài)類型的函數(shù)接口。
2.5 函數(shù)接口
什么是函數(shù)接口,函數(shù)接口是只有一個(gè)抽象方法的接口,用作Lambda表達(dá)式的類型。
看例子:
public interface OnTestListener {
void onTestClick(int x);
}
OnTestListener只有一個(gè)抽象方法:onTestClick(onTestClick定義在OnTestListener接口中,所以默認(rèn)是抽象的,所以可以省略abstract),接受一個(gè)int 參數(shù),并返回空。
這就是使用只有一個(gè)方法的接口來(lái)表示特定的方法并能夠被反復(fù)的使用。接受的參數(shù)類型和數(shù)量,返回的類型都不重要,重要的事只有一個(gè)抽象方法。
2.6 總結(jié)
1.Lambda表達(dá)式是一個(gè)匿名的方法,傳遞一個(gè)代表某種行為的方式。
2.Lambda表達(dá)式有多種表現(xiàn)形式,常用形式:
final String name = "仁昌居士";
Button button = (Button) findViewById(R.id.btn);
button.setOnClickListener(view -> System.out.println(name2+"喜歡御姐"));
參數(shù)變量的類型聲明可顯式可隱式。
3.Lambda表達(dá)式里引用的到的變量是final的,本質(zhì)是值,不是變量。例子如上。
4.Lambda表達(dá)式的類型是個(gè)函數(shù)接口,即僅有一個(gè)抽象方法的接口。
2.7編寫(xiě)理由
曾答應(yīng)過(guò)御姐兒要把她不懂的東西寫(xiě)成博客給她看,所以我就好好的寫(xiě)幾個(gè)系列吧,此次系列JAVA8函數(shù)式編程,會(huì)寫(xiě)完但不定期更新。