1. 程式人生 > >Java程式設計之委託代理回撥、內部類以及匿名內部類回撥(閉包回撥)

Java程式設計之委託代理回撥、內部類以及匿名內部類回撥(閉包回撥)

最近一直在看Java的相關東西,因為我們在iOS開發是,無論是Objective-C還是Swift中,經常會用到委託代理回撥,以及Block回撥或者說是閉包回撥。接下來我們就來看看Java語言中是如何實現委託代理回撥以及閉包回撥的。當然這兩個技術點雖然實現起來並不困難,但是,這回調在封裝一些公用元件時還是特別有用的。所以今天,還是有必要把Java中的委託代理回撥以及閉包回撥來單獨的拿出來聊一下。

本篇部落格我們依然依託於例項,先聊聊委託代理回撥的實現和使用場景,然後再聊一下使用匿名內部類來進行回撥,其實就是我們常說的“閉包”回撥。閉包回撥的實現方式其實就是匿名內部類的使用。既然本篇部落格我們使用到了匿名內部類,我們就再聊一下Java中的內部類

的相關東西。

一、委託代理回撥

在iOS開發中,我們經常使用到委託代理回撥,想TableView、CollectionView等等,這些高階控制元件會依賴於委託回撥來完成一些配置。當然在Java中委託代理回撥也是非常有用的,接下來我們就來看一下Java中的委託代理回撥。當然在Swift或者OC中的委託代理回撥是依託於“協議”的,Swift或者OC中的“協議”其實就是Java語言中的“介面”。所以在Java中的委託代理回撥,依然要依託於“介面”來實現。

1、類圖

首先我們給出該部分例項的類圖,然後我們根據下方的類圖來設計實現我們的具體程式碼。下方就是本部分所設計Demo的類圖,當然,從類圖中我們也能直觀的看到,該示例是比較簡單的,一共也就是一個介面兩個類。CustomDelegate

這個介面是代理類要實現的介面,其中包含了代理類要實現的方法。

從下方的類圖中我們可以看出,代理類FirstClass實現了CustomDelegate代理介面,並實現了相關的代理方法。而SecondClass依賴於CustomDelegate介面,也就是說只要是實現了CustomDelegate介面的類都可以作為SecondClass的代理。而FirstClass中含有SecondClass型別的屬性,並且FirstClass又實現了CustomDelegate介面,在FirstClass中,我們將secondClass物件的代理類指定為FirstClass,稍後我們在具體實現時將會介紹到。

  

2、程式碼的具體實現

根據上述類圖,我們很容易的就可以給出相應的程式碼實現。接下來我們就根據上述類圖來給出具體的程式碼實現。

(1)、CustomDelegate的程式碼實現

下方程式碼段就是CustomDelegate的具體實現。當然該介面的實現比較簡單,就一個setValue(String value)方法。該方法的具體作用是用來相應引數回撥的。下方我們會用到該方法。

package com.zeluli.callback.delegate;

public interface CustomDelegate {
    public void setValue(String value);
}

(2)、SecondClass的程式碼實現

CustomDelegate實現完畢後,接下來我們就來實現一下SecondClass的具體程式碼。下方程式碼段就是SecondClass的具體程式碼實現了。我們從具體實現中可以明確看出,SecondClass類中有個私有的delegate屬性,該屬性是CustomDelegate型別的,所以SecondClass依賴於CustomDelegate型別。

在SecondClass的構造方法中,我們為delegate指定了具體的物件,然後呼叫了begin()方法。begin()方法中做的事情也是比較簡單的,就是使用了Java中自帶的定時器,然後在特定時間的間隔中執行delegate物件的setValue()方法,並且將當前的時間傳給setValue()方法。

package com.zeluli.callback.delegate;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class SecondClass {
    private CustomDelegate delegate;

    public SecondClass(CustomDelegate delegate) {
        this.delegate = delegate;
        this.begin();
    }
    
    public void begin() {
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                delegate.setValue(getNowDate());    //執行委託代理回撥方法
            }
        };
        
        long delay = 0;  
        Timer timer = new Timer();  
        timer.scheduleAtFixedRate(task, delay, 1000); 
    }
    
    private String getNowDate() {
       Date currentTime = new Date();
       SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       String dateString = formatter.format(currentTime);
       return dateString;
    }
}

(3)、FirstClass的建立

接下來我們來建立委託代理類,也就是我們的FirstClass類。其中的程式碼也是比較簡單的,FirstClass類實現了CustomDelegate的相關方法,然後為secondClass物件指定了代理物件就是當前類的物件。具體程式碼如下所示。

 1 package com.zeluli.callback.delegate;
 2 
 3 public class FirstClass implements CustomDelegate {
 4     private SecondClass secondClass;
 5     
 6     public void beginRunSecondDelegateMethod() {
 7         if(this.secondClass == null) {
 8             this.secondClass = new SecondClass(this);
 9         }
10     }
11 
12     //secondClass回撥要執行的方法
13     @Override
14     public void setValue(String value) {
15         System.out.println("第二個類回撥過來的值:" + value);
16     }
17 
18 }

3、測試用例和執行結果

接下來我們來看一下上述程式碼的測試用例和執行結果。下方程式碼段就是我們的測試用例,程式碼比較簡單,就是例項化了一個FirstClass的類物件firstObj,然後呼叫相應的方法為其中的secondClass指定代理方法即可,具體如下所示。

package com.zeluli.callback.delegate;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        FirstClass firstObj = new FirstClass();
        firstObj.beginRunSecondDelegateMethod();
    }
}

下方就是上述程式碼的執行結果,我們可以看出定期會執行FirstClass中的setValue()方法。

  

二、閉包回撥

上面我們實現了委託代理回撥,接下來我們來對上述示例進行改造。將其改成匿名內部類的實現方式,也就是使用閉包的形式來實現回撥。我們只需要講FirstClass進行修改即可。將其委託代理回撥修改成閉包回撥的形式。下方程式碼段就是我們修改後的FirstClass類的原始碼。

從下方的原始碼可以看出,FirstClass並沒有實現CustomDelegate介面。在為SecondClass的物件指定委託代理物件時,我們傳入的是一個匿名內部類的物件,而這個物件的型別是CustomDelegate。這種用法,也是匿名內部類的使用方式之一。

  

修改後的程式碼的測試用例以及執行結果與之前第一部分的委託代理回撥的方式一致,在此就不做過多贅述了。

三、內部類

既然,上述我們使用到了匿名內部類,那麼接下來的這部分我們就來看看內部類的相關內容。內部類,顧名思義,就是定義在介面、類、方法等結構的內部的類。而匿名內部類,就是沒有名字的內部類,這一點也是比較好理解的。下方我們分別從迭代器的示例以及工廠模式的示例中來窺探一下內部類的具體使用場景及使用規則。當然這兩個示例所針對的內部類的角度不同。

1、迭代器中的內部類

在之前的部落格中,我們詳細的聊了迭代器模式,詳細內容請移步於《》。當然之前的迭代器我們是使用的Swift3.0來實現的,今天部落格中我們就用Java的內部類來實現一個Java中的迭代器。

(1)、迭代器介面

按照之前的介紹迭代器的套路,我們還是先要建立迭代器介面的。下方的Selector就是我們建立的迭代器介面。

  • end()方法用來判斷序列是否到達了結尾處。
  • current()方法則用來獲取當前序列中下標的值。
  • next()方法則是移動下標到下一個位置。

為了統一迭代器使用規範性,所有的迭代器都要遵循該介面。具體程式碼如下所示。

  

(2)、建立序列類以及迭代器內部類

下方建立的就是我們的序列類Sequence,該類中的items陣列用來儲存元素,而next屬性指向當前值的下標。在Sequence類中,除了屬性、構造器以及方法外,我們還在其中定義了一個內部類SequenceSelector

SequenceSelector類就是Sequence類的迭代器,並且SequenceSelector要實現迭代器介面Selector。下方我們要注意的一點,在內部類SequenceSelector中,可以直接訪問外層類Sequence類的成員屬性和方法。因為無論是內部類還是Sequence類的成員屬性,都在Sequence類的域中。

當然下方的程式碼的邏輯是比較簡單的,主要是對items陣列的操作。具體程式碼如下所示。

  

(3)、上述迭代器的使用

定義完迭代器後,接下來,我們就來看一下迭代器的使用呢。首先我們建立一個序列物件,然後通過for迴圈往這個序列物件裡邊新增物件。緊接著我們從這個序列物件中獲取其對應的迭代器物件,然後操作迭代器對序列進行遍歷。具體操作如下所示。

  

2、工廠模式中的匿名內部類

聊完迭代器的內部類,接下來我們來看一下工廠模式中的匿名內部類。在之前的部落格中,我們詳細的聊了工廠模式的具體內容,詳情請移步於《》。本篇部落格我們就來看一下,匿名內部類在工廠模式中的使用。

(1)、類圖

首先我們來看一下本部分所涉及案例的具體類圖,下方就是我們當前要介紹內容的類圖。

  • Service介面:首先我們來看一下Service介面,該介面是所有具體的實現類要實現的介面。其中定義這具體的方法宣告。我們的實現類都要繼承自該介面。
  • ServiceFactory介面:該介面是所有工廠類要實現的介面,因為本部分我們的工廠類是以匿名內部類的形式來體現的,所有該介面就是我們“匿名內部類”的型別。
  • Implemention1、2類:這兩個類就是我們的具體實現類,我們的工廠就負責例項化這兩個類。
  • Factories類:該類就負責呼叫工廠方法來建立相關例項,並執行例項的相關方法。

  

(2)、Service和ServiceFactory介面的具體實現

這兩個介面的實現程式碼比較簡單,在此就不做過多贅述了,具體程式碼如下所示:

 1 package com.zeluli.innerclass.factory;
 2 
 3 public interface Service {
 4     void method1();
 5     void method2();
 6 }
 7 
 8 ======================================================
 9 
10 package com.zeluli.innerclass.factory;
11 
12 public interface ServiceFactory {
13     Service getService();
14 }

(3)、Implementation相關類的實現

Implementation1Implementation2的實現差不多,我們就聊一下Implementation1類的具體程式碼。從下方程式碼片段中我們可以看出Implementation1類實現了Service介面,並且給出了介面中相關方法的實現。並且在Implementation1類中有一個ServiceFactory型別的靜態變數factory。而factory引用的是一個ServiceFactory型別的匿名內部類的物件。該匿名內部類就是一個工程類,其中有一個方法負責建立當前外圍類,也就是Implementation1類的物件。具體實現如下所示。

  

(4)、Factory類的實現

接下來我們就來看看Factory類的實現,Factory中就負責從工廠中獲取相應的物件,然後執行物件的相關方法,程式碼比較簡單,就不做過多贅述了。

  

(5)、測試用例與執行結果

接下來我們來看一下上述例項的測試用例以及輸出結果,如下所示:

  

今天的部落格就先到這兒,下篇部落格會繼續聊Java的相關東西。