天天看點

Java—内部類(二)—實作閉包與回調

前言:

Java的閉包與回調我也是第二次接觸這個概念,之前在自學Android的時候繪制View很多地方都用到了監聽器回調,一直不是很明白,現在回頭鞏固Java的基礎總算的弄明白,盡量用我自己了解的語言來和大家分享,希望對不懂的朋友可以有一定的幫助,大神也可以給我一點指點。

概念:

我覺得在了解一個事物之前,需要對這個事物在我們的大腦裡有一個初步的概念,然後再對這個概念補充上細節,這是我在了解一些陌生事物的時候的一個方法,也可以說是類比了解法吧。先說閉包~

一.閉包

閉包,故名思意就是,把一個包關起來,那麼對于Java來說,這個包就是類了,因為在java中任何事物都是類,都是對象。那麼閉包,直接了解上就是把一個類封裝起來(封裝就是包裝差不多的意思)。然後結合一下,閉包内容放在内部類中,是以閉包就是用一個類把另一個類包裝起來,說起來這和内部類沒有什麼不同點啊,為啥要專門用一個詞來表述它呢?因為這個閉包還有很多其他的作用。而且其構造要比内部類複雜一點,先說說它的作用,作用有二~

1. 閉包能夠保護内部類裡面的變量安全,不會被外部通路

2. 閉包能夠維持一個變量一直存活在記憶體中,不被CG(垃圾回收機制)回收掉

在構造上,内部類需要提供一個給外部調用它的接口,這樣才能在維持住内部類的同時,因為内部類攜帶了外部類的資訊,是以外部類也得以存活。

二.回調

回調直接了解就是回頭調用,先将相關的方法實作好,但是并不由我來決定什麼時候來調用它,而是等到一個時候,程式自己回頭調用這個方法,而實作回調機制,這可以說是一種設計模式,而不僅僅是一個語言上的特性。與回調相關的概念還有同步調用,與異步調用,同步調用即單向調用,調用方等待對方執行完成後才傳回。異步調用則類似消息機制,等待收到一定的消息後執行某些操作,回調與異步調用有一些共同之處,現在了解的還不是很清楚,先埋下一坑,以後清楚了在補一篇。

執行個體:

接下來就直接上代碼分析,讓大家補充一些細節上的了解,來清楚整個過程,整個過程相當于我的口述,如有不對的地方,希望大家指出:

interface Incrementable{
    void increment();
}

class Callee1 implements Incrementable{
    private int i = ;
    public void increment(){
        i++;
        System.out.println(i);
    }
}

class MyIncrementable {
    public void increment(){ System.out.println("Other Operarion"); }
    static void f(MyIncrementable mi){ mi.increment(); }
}

class Callee2 extends MyIncrementable{
    private int i = ;
    public void increment(){
        super.increment();
        i++;
        System.out.println(i);
    }
    private class Closure implements Incrementable{
        public void increment(){
            Callee2.this.increment();
        }
    }
    Incrementable getCallbackReference(){
        return new Closure();
    }
}

class Caller{
    private Incrementable callbackReference;
    Caller(Incrementable cbn){ callbackReference = cbn; }
    void go(){ callbackReference.increment(); }
}

public class Callbacks {
    public static void main(String[] args){
        Callee1 c1 = new Callee1();
        Callee2 c2 = new Callee2();
        MyIncrementable.f(c2);
        Caller caller1 = new Caller(c1);
        Caller caller2 = new Caller(c2.getCallbackReference());
        caller1.go();
        caller1.go();
        caller2.go();
        caller2.go();
    }

}
           

這個是java程式設計思想上很經典的一個例子。輸出是這樣的:

Other Operarion
1
1
2
Other Operarion
2
Other Operarion
3
           

希望大家首先自己通讀一下代碼,然後了解一下程式輸出結果為什麼是這樣的,這樣有助于我們去了解之前所說的閉包與回調機制。

這裡我預設大家看完了,我來說一下我的了解:

首先Callee1是一個簡單的實作了接口Incrementable與相關方法,在這裡起到一個對比的作用而已。然後實作了一個MyIncrement類同樣實作了一個increment()方法但是這個與接口中的increment()沒有任何關系,因為這個類自己實作的,并沒有實作這個接口,而靜态方法f()也隻是為了測試一下increment()方法。而Callee2繼承自這個類。這裡就是重點了。同樣寫了一個increment()方法,覆寫了父類方法,但是中間還是調用了父類方法。接下裡是一個内部類也就是閉包的具體實作了。内部類實作了接口Incrementable并且直接調用外部類的方法作為具體的實作。内部類實作Increment able接口很關鍵,這樣就給外部留下了一個通道,能夠接受這個内部類。最後Callee2的後面留下了一個鈎子,即getCallbackReference()方法,它傳回一個内部類的對象,實作了内部與外部的連結,同時有保證了内部類的安全,因為隻有Callee2的對象可以通路與調用這個内部類的方法,而其他的類都無權通路,即使是基類接口對象。而後面的Caller類起到的是一個喚醒作用,通過接受不同的接口對象,實作不同的操作,但還有一個作用是等待接受一個内部類對象,來産生回調。現在大家再回頭看一下輸出就能夠明白了。

假裝你回頭看了,在main()方法中,首先是建立對象與聲明,然後是調用了一個MyIncrement的靜态方法,傳入的是一個Callee2對象,此時無法觸發回調,是以隻是正常的輸出,然後,才Caller2的初始化時傳入的是一個Closure對象進而産生了回掉。

以上就是java的閉包與回調機制,結合後面的内容會有更多意想不到的作用~