鏈?zhǔn)秸{(diào)用
首先需要提一下最近幾年在開發(fā)界大??的鏈?zhǔn)秸{(diào)用,鏈?zhǔn)秸{(diào)用非常利于編寫,更利于閱讀,那么,什么是鏈?zhǔn)秸{(diào)用呢,為了掃盲,舉個(gè)例子。
示例代碼如下,如果我們想創(chuàng)建一個(gè)Dialog,不依賴鏈?zhǔn)秸{(diào)用的話,將會(huì)產(chǎn)生如下代碼:
NRStandardDialog.Builder nrStandardDialogBuilder = NRDialog.standard();
nrStandardDialogBuilder.setTitle("Test");
nrStandardDialogBuilder.setMessage("Test");
nrStandardDialogBuilder.setNegativeTitle("Test");
nrStandardDialogBuilder.setPositiveTitle("Test");
nrStandardDialogBuilder.setNeutralTitle("Test");
nrStandardDialogBuilder.show(this);
上述代碼共計(jì)372個(gè)字符,當(dāng)然,通過(guò)依賴強(qiáng)大的IDEA,我們站在巨人的肩膀上操作,通過(guò)智能提示和自動(dòng)補(bǔ)全,極大降低了開發(fā)成本。
那么,現(xiàn)在來(lái)估算一下操作成本,通過(guò)FreeKey記錄鍵盤輸入,我們的操作路徑大概是
“NRdia .sta ; nr .set "Test??; nr .setM "Test??; nr .setnet ""?Test??; nr .setPo ""?Test??; nr .setneu? "Test??; nr .sh thi ?;”
共計(jì)需要輸入126個(gè)字符,What?這樣看好像其實(shí)也沒(méi)有比全部輸入少很多。
那么,我們將這個(gè)Dialog改為鏈?zhǔn)秸{(diào)用模式創(chuàng)建,會(huì)產(chǎn)生如下代碼。
NRDialog.standard()
.setTitle("Test")
.setMessage("Test")
.setNegativeTitle("Test")
.setPositiveTitle("Test")
.setNeutralTitle("Test")
.show(this);
上述代碼共計(jì)223個(gè)字符,減少了149個(gè)字符。怎么樣,代碼數(shù)量是不是一下子掉下來(lái)了很多。
那么,我們來(lái)統(tǒng)計(jì)一下鏈?zhǔn)秸{(diào)用的操作路徑成本,再次通過(guò)FreeKey記錄鍵盤輸入,我們的操作路徑大概是:
NRD .st .sett "Test??.setme "Test??.setneg "Test??.setpo? "Test??.setNe???? "Test??.sho thi ?;
共計(jì)需要輸入94個(gè)字符,通過(guò)鏈?zhǔn)秸{(diào)用,我們節(jié)省了1.5倍的開發(fā)時(shí)間成本,并且隱形的節(jié)省了代碼的閱讀成本。
方法鏈的優(yōu)秀案例
一些流行的開源庫(kù)的方法鏈優(yōu)秀使用案例如下:
RxJava
Flowable.range(1, 10)
.observeOn(Schedulers.computation())
.map(v -> v * v)
.blockingSubscribe(System.out::println);
EventBus
EventBus.builder()
.eventInheritance(false)
.logSubscriberExceptions(false)
.build()
.register(this);
靜態(tài)方法的鏈?zhǔn)秸{(diào)用
有些時(shí)候,我們會(huì)遇到一些需求,考慮想把靜態(tài)方法進(jìn)行封裝一下,來(lái)實(shí)現(xiàn)優(yōu)美的鏈?zhǔn)秸{(diào)用,Like this;
public class Test {
public static Test doSth(){
// dosth.
return this;
}
}
如果是非靜態(tài)方法的話,我們可以從容的返回this,如果是靜態(tài)方法,這么操作,真的就是想太多。
或者我們這么操作?
這么操作?
R U OK?
一頓操作后,我們還是沒(méi)法實(shí)現(xiàn)靜態(tài)方法的鏈?zhǔn)秸{(diào)用,因?yàn)樗仨毞祷匾粋€(gè)自身的實(shí)例。
好吧,最終屈服于IDEA,你返回了這個(gè)靜態(tài)方法的實(shí)例。
創(chuàng)建了一個(gè)Test類的實(shí)例,然后愉快地實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用
public class MainTest {
public void test(){
Test.doSth().doSth2();
}
}
可是問(wèn)題來(lái)了,為了實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,我們必須為每個(gè)對(duì)象開辟內(nèi)存空間嗎?
當(dāng)然不!
姿勢(shì)來(lái)了,其實(shí)我們可以這樣處理:
public class Test {
public static Test doSth(){
return null;
}
public static Test doSth2(){
return null;
}
}
定義兩個(gè)靜態(tài)方法,返回為空
public class MainTest {
public void test(){
Test.doSth().doSth2();
}
}
完美實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,而且不會(huì)報(bào)空指針,這是為什么?
原理分析
我們對(duì)上述調(diào)用代碼的示例進(jìn)行編譯與反編譯:
public void test(){
Test.doSth().doSth2();
}
對(duì)應(yīng)的反編譯后的字節(jié)碼如下:
public void test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: invokestatic #2 // Method com/Test.doSth:()Lcom/Test;
3: pop
4: invokestatic #3 // Method com//Test.doSth2:()Lcom/Test;
7: pop
8: return
LineNumberTable:
line 9: 0
line 10: 8
invokestatic調(diào)用靜態(tài)方法doSth和doSth2,可以直接找到索引2和索引4的方法,所以并沒(méi)有依賴Test實(shí)例。
而對(duì)于正常的對(duì)象調(diào)用,如果使用Null調(diào)用為什么會(huì)報(bào)空指針呢?我們?cè)賮?lái)看一段示例代碼和其反編譯的字節(jié)碼。
聲明mainTest為空并調(diào)用其test方法。
public void test2(){
MainTest mainTest = null;
mainTest.test();
}
反編譯后的字節(jié)碼如下,invokevirtual為調(diào)用實(shí)例方法,動(dòng)態(tài)綁定,invokevirtual #4代表會(huì)調(diào)用索引4的方法,即test()。
public void test2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=1
0: aconst_null // 將null入棧
1: astore_1 // 將引用存入局部變量表1中
2: aload_1 // 將局部變量表1的對(duì)象引用壓入棧頂
3: invokevirtual #4 // Method com/MainTest.test:()V
6: return
LineNumberTable:
line 25: 0
line 26: 2
line 27: 6
而invokevirtual則負(fù)責(zé)找到操作棧棧頂元素所指向的對(duì)象并查找和調(diào)用其相關(guān)方法,但此時(shí)棧頂為空,自然就無(wú)法找到相關(guān)方法,拋出空指針。
而這里就涉及到了靜態(tài)綁定和動(dòng)態(tài)綁定。綁定過(guò)程,即指方法的調(diào)用與其類的關(guān)聯(lián)過(guò)程。
其中的靜態(tài)綁定,是在方法執(zhí)行前,已經(jīng)被關(guān)聯(lián),而動(dòng)態(tài)綁定則是在運(yùn)行時(shí)綁定。
最后
最后,在項(xiàng)目代碼中還是并不推薦這種方式,團(tuán)隊(duì)開發(fā),難免會(huì)有小伙伴們產(chǎn)生誤解,會(huì)認(rèn)為該對(duì)象已經(jīng)生成了實(shí)例了,而創(chuàng)建非靜態(tài)方法調(diào)用導(dǎo)致空指針。