java編程思想讀書筆記 第十章 內部類(下)

1.內部類的繼承
因爲內部類的構造器必須連接到指向其外圍類對象的引用,所以在繼承內部類的時候,必須使用特殊的語法來明確說清它們之間的關聯:

public class WithInner {
    class Inner{
    }
}public class IntheritInner extends WithInner.Inner{
    IntheritInner(WithInner wi){
        wi.super();
    }
    public static void main(String[] args) {
        WithInner withInner = new WithInner();
        IntheritInner ii = new IntheritInner(withInner);
    }
}

可以看到,IntheritInner只繼承自內部類,而不是外圍類。但是當要生成一個構造器時,默認的構造器並不算好,而且不能只是傳遞一個指向外圍類對象的引用。此外,必須在構造器內使用如下語法:enclosingClassReference.super();這樣才提供了必要的引用,纔不會報錯。

2.內部類可以爲覆蓋嗎
創建一個內部類,然後繼承其外圍類並重新定義此內部類,也就是“覆蓋”內部類,但是內部類就好像是外圍類的一個方法,其實並不起什麼作用。例子如下:

public class Egg {
    private Yolk y;
    protected class Yolk{
        public Yolk() {
            System.out.println("Egg.Yolk()");
        }
    }
    public Egg() {
        System.out.println("new Egg()");
        y = new Yolk();
    }
}
public class BigEgg extends Egg{
    public class Yolk{
    public Yolk() {
        System.out.println("BigEgg.Yolk()");
        }   
    }
    public static void main(String[] args) {
        new BigEgg();
    }

}
輸出:new Egg()
           Egg.Yolk()

這個例子說明,當繼承了某個外圍類的時候,內部類並沒有發生什麼特別神器的變化。這兩個內部類時完全獨立的兩個實體,各自在自己的命名空間內。

3.局部內部類
可以在代碼塊裏創建內部類,典型的方式是在一個方法體裏面創建內部類。局部內部類不能有訪問說明符,因爲它不是外圍類的一部分;但是它可以訪問當前代碼塊內的常量,以及此外圍類的所有成員。下面的例子對局部內部類與匿名內部類的創建進行了比較,例子如下:

public interface Counter {
    int next();
}
public class LocalInnerClass {
    private int count = 0;
    Counter getCounter(final String name){
        class LocalCounter implements Counter{
            public LocalCounter(){
                System.out.println("LocalCounter()");
            }
            @Override
            public int next() {
                System.out.println(name);
                return count++;
            }
                    }       
        return new LocalCounter();
    }
    Counter getCounter2(final String name){
        return new Counter() {
            {
                System.out.println("Counter()");
            }
            @Override
            public int next() {
                System.out.println(name);
                return count++;
            }
        };
    }
    public static void main(String[] args) {
        LocalInnerClass lic = new LocalInnerClass();
        Counter c1 = lic.getCounter("Local inner");
        Counter c2 = lic.getCounter2("Anonymous inner");
        for (int i = 0; i < 5; i++) {
            System.out.println(c1.next());
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(c2.next());
        }
            }
}

Counter 返回的是系列中的下一個值。我們分別使用局部內部類和匿名內部類實現了這個功能,它們具有相同的行爲和能力。既然局部內部類的名字在方法外是不可見的,那爲什麼我們仍然使用局部內部類而不是匿名內部類呢?唯一的理由是,我們需要一個已命名的構造器,或者需要重載構造器,而匿名內部類只能用於實例初始化。所以使用局部內部類而不使用匿名內部類的另一個理由就是,需要不止一個該內部類的對象。

4.爲什麼加上final後的局部變量就可以在內部類中使用了?
因爲加上final後,編譯器是這樣處理內部類的:如果這個外部局部變量是常量,則在內部類代碼中直接用這個常量;如果是類的實例,則編譯器將產生一個內部類的構造參數,將這個final變量傳到內部類裏,這樣即使外部局部變量無效了,還可以使用,所以調用的實際是自己的屬性而不是外部類方法的參數或局部變量。這樣理解就很容易得出爲什麼要用final了,因爲兩者從外表看起來是同一個東西,實際上卻不是這樣,如果內部類改掉了這些參數的值也不可能影響到原參數,然而這樣卻失去了參數的一致性,因爲從編程人員的角度來看他們是同一個東西,如果編程人員在程序設計的時候在內部類中改掉參數的值,但是外部調用的時候又發現值其實沒有被改掉,這就讓人非常的難以理解和接受,爲了避免這種尷尬的問題存在,所以編譯器設計人員把內部類能夠使用的參數設定爲必須是final來規避這種莫名其妙錯誤的存在。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章