1. 程式人生 > >Java內部類(2): 用法和回撥

Java內部類(2): 用法和回撥

為什麼需要內部類

一般來說,內部類繼承自某個類或實現了某個介面,內部類程式碼操作建立它的外部類的物件,可以說,內部類提供了某種進入外部類的視窗,而且內部類自己本身實現了有效的程式碼隱藏。
內部類必須要回答的一個問題是:如果只是需要一個介面的引用,為什麼外部類不直接實現介面呢。答案是:如果這能滿足需求,就應該這麼做!那麼內部類實現一個介面於外部類實現這個介面有什麼區別呢?答案是:後者並不總能享受介面帶來的便利,有時候需要介面的實現。使用內部類最吸引人的地方在於:
外部類可以有多個內部類,每個內部類都能夠獨自實現一個介面(繼承一個類),且無論外圍類是否實現該介面(繼承該類)都不會對內部類產生影響。

  • 我們考慮這樣一種情況,在一個類中需要以某種方式實現兩個介面A和B。這兩個介面相互獨立,彼此直接沒有任何聯絡。那麼有兩種方式:

    1. 直接實現兩個介面
    2. 該類實現介面A,另外構建一個內部類實現介面B
      一般情況下兩種方式都可以,但對於第二種方式來說,它把兩個介面的實現完全分離,並且可以隱藏B介面實現的細節。
      拿HashMap舉例,就是通過內部類來實現Iterator介面的,既實現了hashmap的遍歷功能,又保證了得到的遍歷器能夠訪問原來的map物件並且對外部來說完全隱藏了遍歷器的實現細節。如果現在需要hashmap實現另一個反向遍歷介面ReIterator,那麼只需要再定義一個內部類實現反向遍歷介面,通過方法返回內部類例項就可以,並不需要修改外部類的實現介面。

    另一種情況:如果好巧不巧的,A、B兩個介面有一個相同的方法(雖然方法名相同,但完全獨立,是兩個互不關聯的方法),那麼第一種方式就不可行了,因為你只能在類中定義一個該方法,無法區分屬於哪個介面。

  • 另外一點:可以定義多個內部類,並且這些內部類相互獨立。考慮這樣一種情況,在一個類中需要以某種方式實現多個介面(5個以上並且每個介面的方法很多)那麼,如果直接實現這些介面,那麼類中的方法數量將會很多,類會變得很龐大,這也不符合程式碼分離的理念。
    從某種程度上來說,內部類完善了java的多繼承

  • 在單個外部類中,可以讓多個內部類以不同的方式實現同一個介面或繼承同一個類。就像HashMap,我們可以以多種方式實現遍歷操作。

內部類與回撥

回撥用於層間協作。例如一個驅動,是一個地層。但有資料傳給它時,它不僅要處理自己本層的工作,還要進行回撥,將資料傳給它的上層供上層處理。
回撥跟API有點相似,都是跨層級呼叫的。不過與API不同的是,API是下層提供給上層去呼叫的,那麼上層主動去呼叫下層API,這叫Call,上層是知道所呼叫的下層方法的。但回撥不同,回撥是上層提供給下層去呼叫的,而且具體方法下層是不知道的,下層只知道自己需要在某些條件下呼叫這個方法。上層向下層注入自己的程式(比如一個物件),然後下層通過這段程式(這個物件)來反過來呼叫上層的方法,這叫回調。

而Java的回撥呢,就是通過介面和內部類來實現的。通常是別人提供一個介面,我用內部類實現這個介面並將內部類的一個例項注入到(傳遞給)別人的程式,別人的程式再通過我傳入的示例來呼叫方法。
舉例說明:
//

/** ICallBack介面和Print類就好比是別人提供的程式 **/
//介面類
public interface ICallBack {
    public void execute();
}


public class Printer {
    ICallBack icb;
    public Printer(ICallBack icb){
        this.icb = icb;
    }

    //提供給外部去呼叫的。即提供一個介面,我不去具體實現它,由外部程式來實現。再在想要的時候去回撥外部的方法。我沒有實現介面,但我擁有一個是實現了介面的外部物件,我通過外部物件去呼叫外部的方法。
    public void print(){
        System.out.println("print start ========");//固定部分
        icb.execute(); //抽象部分,由外界去實現
        System.out.println("print end ========");//固定部分
    }
}


/PrintHandle相當於外部類
public class PrintHandle {
    public void printf(){
        System.out.println("printHandle print");
    }

    public void go(){//建立了一個匿名類物件並傳遞給Printer,通過內部類Printer也可以呼叫我的printf()方法了,這叫回調
        Printer print = new Printer(new ICallBack() {
            @Override
            public void execute() {
                printf();
            }
        });
        print.print();
    }
    public static void main(String[] args) {
        PrintHandle printHandle = new PrintHandle();
        printHandle.go();
    }

}

另外再寫一個測試方法執行時間的類

public class TestTime {
    public Long getTime(ICallBack icb){
        long start = System.currentTimeMillis();
        icb.execute();
        long end = System.currentTimeMillis();
        return end-start;
    }
}

//現在我要測試執行一遍test()方法需要多長時間
public class TestTimeMain {
    public void test() {//需要測試的方法
        List<Map<String,String>> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++){
            HashMap map = new HashMap<String,String>();
            list.add(map);
        }
    }

    public static void main(String[] args) {
        TestTimeMain main = new TestTimeMain();
        TestTime testTime = new TestTime();
//測試test()方法執行的時間,只要將這個方法放到匿名內部類的execute()裡面就可以了
        long time = testTime.getTime(new ICallBack() {
            @Override
            public void execute() {
                main.test();
            }
        });
        System.out.println(time);
    }
}

回撥的另一種方式:我稱之為自身回撥。
有兩個介面worker(工人)和programmer(程式設計師),這兩個介面都有一個work()方法,現有一個員工,要求它以某種方式實現這兩個介面。這隻能通過內部類來實現

Interface Worker{
    void work();
}

Interface Programmer{
    void work();
}

class Employee implements Worker{
    public void work(){//作為工人在工作
        System.out.println("worker is working");
    }

    public void code(){//作為程式設計師在程式設計
        System.out.println("programmer is programming");
    }
    class Clousure implements Programmer{
        public void work(){
            code();
        }
    }

    private class Clousure implements Programmer{
        public void work(){
            code();
        }
    }

    public Programmer getCallBack(){
        return new Clousure();
    }

    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.work();
        employee.getCallBack().work();
    }
}