迄今為止,我們見到的內部類好象僅僅是一種名字隱藏以及代碼組織方案。盡管這些功能非常有用,但似乎並不特別引人注目。然而,我們還忽略了另一個重要的事實。創建自己的內部類時,那個類的對象同時擁有指向封裝對象(這些對象封裝或生成了內部類)的一個鏈接。所以它們能訪問那個封裝對象的成員——毋需取得任何資格。除此以外,內部類擁有對封裝類所有元素的訪問權限(注釋②)。下面這個例子闡示了這個問題:
//: Sequence.java // Holds a sequence of Objects interface Selector { boolean end(); Object current(); void next(); } public class Sequence { private Object[] o; private int next = 0; public Sequence(int size) { o = new Object[size]; } public void add(Object x) { if(next < o.length) { o[next] = x; next++; } } private class SSelector implements Selector { int i = 0; public boolean end() { return i == o.length; } public Object current() { return o[i]; } public void next() { if(i < o.length) i++; } } public Selector getSelector() { return new SSelector(); } public static void main(String[] args) { Sequence s = new Sequence(10); for(int i = 0; i < 10; i++) s.add(Integer.toString(i)); Selector sl = s.getSelector(); while(!sl.end()) { System.out.println((String)sl.current()); sl.next(); } } } ///:~
②:這與C++“嵌套類”的設計頗有不同,後者只是一種單純的名字隱藏機制。在C++中,沒有指向一個封裝對象的鏈接,也不存在默認的訪問權限。
其中,Sequence只是一個大小固定的對象數組,有一個類將其封裝在內部。我們調用add(),以便將一個新對象添加到Sequence末尾(如果還有地方的話)。為了取得Sequence中的每一個對象,要使用一個名為Selector的接口,它使我們能夠知道自己是否位於最末尾(end()),能觀看當前對象(current() Object),以及能夠移至Sequence內的下一個對象(next() Object)。由於Selector是一個接口,所以其他許多類都能用它們自己的方式實現接口,而且許多方法都能將接口作為一個自變量使用,從而創建一般的代碼。
在這裡,SSelector是一個私有類,它提供了Selector功能。在main()中,大家可看到Sequence的創建過程,在它後面是一系列字串對象的添加。隨後,通過對getSelector()的一個調用生成一個Selector。並用它在Sequence中移動,同時選擇每一個項目。
從表面看,SSelector似乎只是另一個內部類。但不要被表面現象迷惑。請注意觀察end(),current()以及next(),它們每個方法都引用了o。o是個不屬於SSelector一部分的句柄,而是位於封裝類裡的一個private字段。然而,內部類可以從封裝類訪問方法與字段,就象已經擁有了它們一樣。這一特征對我們來說是非常方便的,就象在上面的例子中看到的那樣。
因此,我們現在知道一個內部類可以訪問封裝類的成員。這是如何實現的呢?內部類必須擁有對封裝類的特定對象的一個引用,而封裝類的作用就是創建這個內部類。隨後,當我們引用封裝類的一個成員時,就利用那個(隱藏)的引用來選擇那個成員。幸運的是,編譯器會幫助我們照管所有這些細節。但我們現在也可以理解內部類的一個對象只能與封裝類的一個對象聯合創建。在這個創建過程中,要求對封裝類對象的句柄進行初始化。若不能訪問那個句柄,編譯器就會報錯。進行所有這些操作的時候,大多數時候都不要求程序員的任何介入。