你必定不知道的鏈式調用新姿式

如何鏈式調用Java靜態方法?java

鏈式調用

首先須要提一下最近幾年在開發界大🔥的鏈式調用,鏈式調用很是利於編寫,更利於閱讀,那麼,什麼是鏈式調用呢,爲了掃盲,舉個例子。bash

栗子

示例代碼以下,若是咱們想建立一個Dialog,不依賴鏈式調用的話,將會產生以下代碼:ui

NRStandardDialog.Builder nrStandardDialogBuilder = NRDialog.standard();

    nrStandardDialogBuilder.setTitle("Test");
    nrStandardDialogBuilder.setMessage("Test");
    nrStandardDialogBuilder.setNegativeTitle("Test");
    nrStandardDialogBuilder.setPositiveTitle("Test");
    nrStandardDialogBuilder.setNeutralTitle("Test");

    nrStandardDialogBuilder.show(this);
複製代碼

上述代碼共計372個字符,固然,經過依賴強大的IDEA,咱們站在巨人的肩膀上操做,經過智能提示和自動補全,極大下降了開發成本。this

那麼,如今來估算一下操做成本,經過FreeKey記錄鍵盤輸入,咱們的操做路徑大概是spa

「NRdia .sta ; nr .set "Test; nr .setM "Test; nr .setnet ""Test; nr .setPo ""Test; nr .setneu "Test; nr .sh thi ;」
複製代碼

共計須要輸入126個字符,What?這樣看好像其實也沒有比所有輸入少不少。指針

大腿

那麼,咱們將這個Dialog改成鏈式調用模式建立,會產生以下代碼。code

NRDialog.standard()
            .setTitle("Test")
            .setMessage("Test")
            .setNegativeTitle("Test")
            .setPositiveTitle("Test")
            .setNeutralTitle("Test")
            .show(this);
複製代碼

上述代碼共計223個字符,減小了149個字符。怎麼樣,代碼數量是否是一會兒掉下來了不少。cdn

3.png

那麼,咱們來統計一下鏈式調用的操做路徑成本,再次經過FreeKey記錄鍵盤輸入,咱們的操做路徑大概是:對象

NRD .st .sett "Test.setme "Test.setneg "Test.setpo "Test.setNe "Test.sho thi ;
複製代碼

共計須要輸入94個字符,經過鏈式調用,咱們節省了1.5倍的開發時間成本,而且隱形的節省了代碼的閱讀成本。blog

方法鏈的優秀案例

一些流行的開源庫的方法鏈優秀使用案例以下:

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);
複製代碼

靜態方法的鏈式調用

有些時候,咱們會遇到一些需求,考慮想把靜態方法進行封裝一下,來實現優美的鏈式調用,Like this;

public class Test {

    public static Test doSth(){
        // dosth.
        return this;
    }
}
複製代碼

若是是非靜態方法的話,咱們能夠從容的返回this,若是是靜態方法,這麼操做,真的就是想太多。

4.png

或者咱們這麼操做?

5.png

這麼操做?

6.png

R U OK?

7.png

一頓操做後,咱們仍是無法實現靜態方法的鏈式調用,由於他必須返回一個自身的實例。

鬧太套

好吧,最終屈服於IDEA,你反悔了這個靜態方法的實例。

9.png

建立了一個Test類的實例,而後愉快地實現了鏈式調用

public class MainTest {

    public void test(){
        Test.doSth().doSth2();
    }
}
複製代碼

但是問題來了,爲了實現鏈式調用,咱們必須爲每一個對象開闢內存空間嗎?

10.jpeg

固然不!

姿式來了,其實咱們能夠這樣處理:

public class Test {

    public static Test doSth(){
        return null;
    }

    public static Test doSth2(){
        return null;
    }
}
複製代碼

定義兩個靜態方法,返回爲空

public class MainTest {

    public void test(){
        Test.doSth().doSth2();
    }

}
複製代碼

完美實現鏈式調用,並且不會報空指針,這是爲何?

原理分析

咱們對上述調用代碼的示例進行編譯與反編譯:

public void test(){
        Test.doSth().doSth2();
    }
複製代碼

對應的反編譯後的字節碼以下:

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調用靜態方法doSth和doSth2,能夠直接找到索引2和索引4的方法,因此並無依賴Test實例。

而對於正常的對象調用,若是使用Null調用爲何會報空指針呢?咱們再來看一段示例代碼和其反編譯的字節碼。

聲明mainTest爲空並調用其test方法。

public void test2(){
    MainTest mainTest = null;
    mainTest.test();
}
複製代碼

反編譯後的字節碼以下,invokevirtual爲調用實例方法,動態綁定,invokevirtual #4表明會調用索引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的對象引用壓入棧頂
         3: invokevirtual #4 // Method com/MainTest.test:()V
         6: return
      LineNumberTable:
        line 25: 0
        line 26: 2
        line 27: 6
複製代碼

而invokevirtual則負責找到操做棧棧頂元素所指向的對象並查找和調用其相關方法,但此時棧頂爲空,天然就沒法找到相關方法,拋出空指針。

而這裏就涉及到了靜態綁定和動態綁定。綁定過程,即指方法的調用與其類的關聯過程。

其中的靜態綁定,是在方法執行前,已經被關聯,而動態綁定則是在運行時綁定。

最後

最後,在項目代碼中仍是並不推薦這種方式,團隊開發,不免會有小夥伴們產生誤解,會認爲該對象已經生成了實例了,而建立非靜態方法調用致使空指針。

相關文章
相關標籤/搜索