Java中的巢狀類、內部類、靜態內部類

孫群發表於2015-06-14
在Java中我們在一個類的內部再定義一個類,如下所示:
class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

那麼在上面的例子中我們稱OuterClass為外圍類(enclosing class),裡面的那個類稱之為巢狀類(Nested Class).

巢狀類可以分為兩種,靜態的和非靜態的,即靜態巢狀類和非靜態巢狀類。非靜態巢狀類又叫做內部類(Inner Class)。我們通常所說的靜態內部類其實是不嚴格的,嚴格的說應該叫做靜態巢狀類(Static Nested Class)。

class OuterClass {
    ...
    class InnerClass {
        ...
    }

    static class StaticNestedClass {
        ...
    }    
}

上述程式碼中的InnerClass就是內部類,StaticNestedClass就是靜態巢狀類。

內部類與靜態巢狀類雖然都是巢狀類,但在使用上是有一些區別的。


內部類

比如有如下內部類的定義,

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}
OuterClass是InnerClass的外圍類,InnerClass是OuterClass的內部類。內部類的例項物件都會繫結一個外圍類的例項物件,並且InnerClass可以訪問其所繫結的OuterClass的所有成員屬性以及方法,包括私有成員屬性以及方法。在InnerClass中通過OuterClass.this顯式的引用其所繫結的OuterClass的例項。要例項化內部類InnerClass,必須首先例項化其外圍類OuterClass,然後用如下的語法建立內部類的例項:

OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
注意,上面寫得是outerObject.new InnerClass(),而不是new OuterClass.InnerClass(); 
我們在執行程式碼OuterClass.InnerClass innerObject = outerObject.new InnerClass()的時候,其實做了兩件事,一件事是建立一個內部類的例項innerObject,第二件事是讓innerObject繫結outerObject作為其外圍類的例項。這樣innerObject就可以訪問outerObject內的所有成員屬性以及方法了。
那如果想直接跳過外圍類去初始化內部類會怎麼樣呢?程式碼如下所示:

如果執行程式碼InnerClass innerObject = new InnerClass(),會出現如下的編譯錯誤:
No enclosing instance of type OuterClass is accessible. Must qualify the allocation with an enclosing instance of type OuterClass (e.g. x.new A() where x is an instance of OuterClass).
編譯器給出的錯誤提示很明確,就是我們沒有外圍類OuterClass的例項就去初始化內部類了。要寫成x.new InnerClass()的形式,其中x是外圍類OuterClass的一個例項物件。

靜態巢狀類
有些人把靜態巢狀類成為靜態內部類,其實靜態內部類這個稱呼不嚴謹,因為內部類都是非靜態的。靜態巢狀類與內部類有很大的不同,靜態巢狀類說到底就是一個靜態類,只不過是其位置位於某個類的內部罷了。
假設有如下靜態巢狀類的定義:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
}
那麼我可以像正常使用一個一般的靜態類那樣使用一個靜態巢狀類,只不過要通過其外圍類的名字來訪問靜態巢狀類的名字。所以,外圍類更像是靜態巢狀類的名稱空間。比如要獲取靜態巢狀類,要使用OuterClass.StaticNestedClass。
如果要建立靜態巢狀類的實力物件,使用如下的語法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
由於靜態巢狀類的本質就是一個靜態類,所以其例項物件的初始化不需要也不能像內部類那樣需要繫結一個外圍類物件。由於靜態巢狀類沒有像內部類那樣繫結外部類物件,所以也就不存在靜態巢狀類不能訪問其外圍類的成員這種說法。
如果我們像初始化內部類那樣初始化靜態巢狀類,也就是在建立靜態巢狀類的時候給其繫結其外圍類的例項物件,會怎麼樣呢?程式碼如下所示:


可以發現,如果我們在初始化靜態巢狀類時強行給其繫結外圍類的例項物件,編譯器就會報錯:
Illegal enclosing instance specification for type OuterClass.StaticNestedClass
我們給靜態巢狀類OuterClass.StaticNestedClass指定了非法的外圍類的例項。

綜上所述,雖然內部類和靜態巢狀類都屬於巢狀類,但是二者有本質區別:內部類的例項化物件需要繫結一個外圍類的例項化物件,而靜態巢狀類的例項化物件不能也無法繫結外圍類的例項化物件。

相關文章