標籤:匿名內部類 迭代器 內部類
可以將一個類的定義放在另一個類的定義內部,這就是內部類.
內部類的定義是簡單的,但是它的文法確實很是複雜,讓人不是很好理解.下面就內部類做一個小結.
一.內部類的分類
總的來講內部類分為普通內部類,匿名內部類,局部內部類,嵌套類(靜態內部類)等.下面簡要的介紹以下這些內部類定義的文法.
(1).對於普通的內部類,就是在外圍類中插入另一個類的定義.如下面的代碼:
package lkl1;///封裝一個包裹類public class Parcel { ///在一個類的內部定義的另一個類,稱之為內部類 ///它的定義形式並沒有什麼不同 public class Destination{ private String label; Destination(String whereto){ label=whereto; } public String getLabel(){ return label; } } public class Contents{ private int i=11; public int value(){ return i; } } ///提供一個統一的建立內部類的介面 public Destination destination(String s){ return new Destination(s); } public Contents contents(){ return new Contents(); } public void ship(String dest){ //其實也可以直接調用構造器定義 //Contents c1 = new Contents(); Contents c = contents(); Destination d = destination(dest); System.out.println(d.label); } public static void main(String[] args){ Parcel p = new Parcel(); p.ship("changsha"); Parcel p1= new Parcel(); ///為內部類定義引用,注意名字的層次 ///在非靜態方法中定義內部類的對像需要具體指明這個對象的類型 ///OuterClassName.InnerClassName Parcel.Contents c =p1.contents(); System.out.println(c.i); Parcel.Destination d = p1.destination("wuhan"); }}
(2).所謂的匿名內部類的定義文法比較奇特.匿名內部類是沒有名字的,所以我們不能想一般的類那樣調用構造器得到它的對象,一般我們都將它放在一個方法中,這個方法負責返回這個匿名內部類的一個對象.因為匿名內部類沒有名字,所以也就不能通過構造器來實現初始化了,我們可以通過初始化塊的形式達到構造器的效果(當然我們是可以調用基類的構造器來初始化基類的成員變數).如下面的代碼:
package lkl1;public class Parcel3 { ///用於匿名內部類變數初始化的形參必須要用final修飾 //Destination是前面定義的一個類. public Destination destination(String dest,final double price,int i){ ///建立匿名內部類的一般格式 return new Destination(i){ ////可以通過調用基類的構造器進行基類的初始化 private int cost; {///用初始化塊達到類似於構造器的初始化過程 cost =(int) Math.round(price); if(cost>100){ System.out.println("Over budget!"); } } private String label=dest; public String readLabel(){ return label; } }; } public static void main(String[] args){ Parcel3 p3= new Parcel3(); Destination d = p3.destination("changsha", 109.234,10); System.out.println(d.readLabel()); }}
(3)所謂的局部內部類其實就是在方法和範圍內定義的內部類.這種內部類只在一定的範圍是有效(其實上面的內部類就是一種局部內部類).一般我們是將其當成工具使用的,不希望它是公用可見的.如下面的代碼:
package lkl1;public class Test { private PrintId create(){ ///外部類中的一個方法中定義內部類用於實現某個介面; ///然後返回這個內部類的一個引用 { ///PrintId是前面的定義的一個介面 class Print implements PrintId{ private String id; public Print(){ id="longkaili"; } public void print(){ System.out.println(id); } } return new Print(); } //試圖在範圍外訪問內部類出錯 //(new Print()).id; } public static void main(String[] args){ ///以下說明了雖然內部類是定義在一個範圍類的 ///但是在外面還是可以使用它實現的功能的 Test ts = new Test(); ts.create().print(); ///在本類方法中建立本類的對象,其private介面是可見的 }}
(4).嵌套類其實就是就將內部類聲明成static.對於普通的內部類其必須要依賴於一個外部類的對象,而嵌套類是不需要的,我們可以將其看成一個static型的變數.如下面的代碼:
package lkl1;///當內部類被聲明成靜態時,我們就將其當成一個靜態成員來看待///此時內部類不在和外部類的對象綁定在一起.public class Parcel1 { private static class Parcel1Contents extends Contents{ private int i=1; public int value(){ return i; } } protected static class Parcel1Destination extends Destination{ private String label; private Parcel1Destination(String whereTo){ label=whereTo; } public String readLabel(){ return label; } } //我們可以通過外部類的靜態方法建立嵌套類的對象 public static Destination destination(String s){ return new Parcel1Destination(s); } public static Contents contents(){ return new Parcel1Contents(); } public static void main(String[] args){ Contents c= contents(); Destination d =destination("changsha"); }}
二.外部類和內部類的聯絡.
既然內部類定義在了外部類的內部,那麼肯定就具有一定的聯絡的.具體的我們分成非static型的一般內部類和static修飾的嵌套類來分析.
(1).首先對於一般的內部類,其和外部類的聯絡是很緊密的.體現在內部類的對象必須依附於一個外部類的對象,也就是說我們必須通過一個外部類的對象才能建立內部類的對象.其次,外部類的一切成員變數對於內部類都是可見的,包括private成員變數,而外部類也可以訪問內部類的所有變數.另外,內部類還可以通過.this方式顯式的訪問其對於的外部類對象,外部類也可以通過.new方式建立內部類的對象(一般我們都是通過外部類的一個方法返回內部類的對象引用).下面的代碼示範了這幾點:
package lkl1;///測試外部類對內部類的存取權限public class Outer { private int k=100; private class Inner{ private int i=100; public int j=1111; private void print(){ System.out.println("Outer.k = "+ k); ///內部類可以訪問外部類的所有成員變數 System.out.println("Inner.print()"); } ///非static內部類中不能建立static類型的變數 /// public static int k=999; } public void print(Inner in){ ///事實證明外部類同樣可以訪問內部類的所有方法,變數 ///當然前提是我們有一個內部類的對象,直接通過類名來訪問是不行的 in.print(); in.i++; in.j++; System.out.println(in.i); System.out.println(in.j); } public static void main(String[] args){ Outer ot = new Outer(); Outer.Inner in = ot.new Inner(); ///通過.new建立內部類的引用,注意內部類引用的聲明方式 ot.print(in); }}
(2).對於static修飾的嵌套類來說,情況就不同了.通過上面的例子我們可以看到普通的內部類對象隱式的儲存了一個引用,指向建立它的外圍對象.但對於嵌套類,它的對象不依賴於外部類的對象而存在,當然它也不能訪問非靜態外圍類對象.另外還有一點.普通類中是不能包括static方法,變數的,但是嵌套類中是可以包含這些東西的.如下面的代碼所示:
package lkl1;///當內部類被聲明成靜態時,我們就將其當成一個靜態成員來看待///此時內部類不在和外部類的對象綁定在一起.public class Parcel1 {//ContentsheDestination都是前面定義的抽象類別 private static class Parcel1Contents extends Contents{ private static int k=110; private int i=1; public int value(){ return i; } } //我們可以通過外部類的靜態方法建立嵌套類的對象 public static Contents contents(){ return new Parcel1Contents(); } public static void main(String[] args){ ///訪問Parecel1的靜態變數,注意調用格式 System.out.println("Parcel1Contents的靜態變數k "+Parcel1.Parcel1Contents.k); Contents c= contents(); }}
三.內部類的作用
內部類的文法是很複雜的,但是在學習這些複雜文法的時候更令我迷惑的是:內部類有什麼用?java編程思想一書是這麼講的:
1.每個內部類都能獨立的繼承自一個(介面的)實現,所以無論外圍類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響.
2.可以更好的實現多重繼承.我們知道普通的類是不可以多重繼承的,但是現在通過內部類,我們就可以對普通類達到多重繼承的效果.
3.在一些設計模式中有重要的應用.
對於這些現在還不是都能理解的很清楚,希望在以後的學習中能夠搞清楚.
下面給一個通過內部類實現迭代器的執行個體.下面的Sequence類是一個封裝了一個Object數組的普通類,而Selector是一個通用的迭代器介面,我們在Sequence中通過一個內部類實現了Selector介面,然後通過這個內部類的介面向上轉型後的對象對Sequence對象進行迭代訪問.其實以前我們接觸過得集合類的迭代器也就是這麼實現的.
package lkl1;///定義一個通用的迭代器介面public interface Selector { boolean end(); Object current(); void next();}package lkl1;///Sequence類封裝一個固定大小的///Object數組,然後提供它自己的一個迭代器///這裡利用的是內部類可以訪問外部類的所有資料的性質public class Sequence { private Object[] items; private int next=0;///保留當前元素個數 public Sequence(int size){ items=new Object[size]; } public void add(Object obj){ if(next<items.length){ items[next++]=obj; } } ///封裝一個內部類實現迭代器 private class SequenceSelector implements Selector{ private int i=0; ///當前訪問到元素的編號 public boolean end(){ return i==items.length; } public Object current(){ return items[i]; } public void next(){ if(i<items.length) i++; } } ///提供迭代器對外的介面 public Selector selector(){ return new SequenceSelector(); } public static void main(String[] args){ Sequence sq= new Sequence(10); for(int i=0;i<10;i++) sq.add(Integer.toString(i)); ///利用Sequence本身的迭代器來訪問 Selector se =sq.selector(); while(!se.end()){ System.out.print(se.current()+" "); se.next(); } System.out.println(); }}
Thinking in Java---內部類及一個迭代器執行個體