# 內部類別(inner class) 如果一個類別的{ }之內又定義了其他類別,就稱為(該類別的)內部類別(inner class),而把最外層的類別稱為外部類別(outer class)。  註:若沒有定義內部類別,自然也就沒有區分內、外了,就是我們最常使用的普通類別。 以一個汽車引擎來說,一般人都會把引擎當作是一個物件整體,當故障時我們只會說是引擎故障,請專業人員來維修。不過在專業技師的眼裡,引擎內部還有非常多的內部類別,他明白引擎系統之內還有很多系統,例如電噴系統(或化油器)、火星塞系統、活塞系統、阻風門系統 … 甚至是ECU系統,只要其中一個系統故障,對外呈現出來就是車主說的「引擎故障」。 以這個例子來說,你可以把引擎內部所有的系統都寫成引擎類別的內部類別,也可以不使用內部類別,像我們一般常規的方式,把每個系統都寫成各自的(外部)類別,再組裝成一個引擎整體。 內部類別的程式碼看起來像這樣: class Outer{ class Inner1 { } class Inner2 { } class Inner3 { } … … … } 內部類別可以有很多個(內部類別的個數),而且每一個內部類別又可以再有它自己的內部類別(內部類別的層數),就好像俄羅斯套娃一樣。也就是說,你可以有很多個俄羅斯套娃,而且每一個娃娃內部又都有好幾層。 內部類別(inner class),其實就和巢狀迴圈或巢狀判斷式是類似的概念,所以內部類別也被稱為巢狀類別(nested class)。 如何使用內部類別: 假設我們宣告一個叫做Human的外部類別: package tina; class Human { // 宣告一個外部類別,命名為Human String name; public Human(String str) { // 定義外部類別的建構子,引數為字串型態 this.name = str; } class Wearing { // 定義內部類別,使用它預設的無引數建構子 String color; // 定義內部類別的屬性 char size; } } 以上程式碼在外部類別Human中還定義了內部類別Wearing,那該怎麼使用內部類別Wearing呢? public class HumanTest { // 新宣告一個HumanTest類別 public static void main(String[ ] args){ Human tina = new Human("小婷"); // 使用外部類別創造物件 Human.Wearing wear = tina.new Wearing(); // 使用內部類別創造物件 wear.color = "white"; wear.size = 's'; } } 以上例子可以看到,當我們要使用內部類別來創造物件時(建立內部類別的物件),寫法是這樣: 外部類別 .內部類別 物件名稱(自己取名) = 外部物件 . new 內部類別的建構子(); 這樣的方法和匯入package的觀念是一樣的,也就是以「.」運算子來進入到下一層(內層),在Java中這樣的方式被大量使用。如果你還有更深層的內部類別,依此類推,再多加「.」運算子就可以了。 好,現在我們已經學會創造內部類別的物件了,但感覺好像多此一舉?也就是說,到底為什麼要使用內部類別? 透過外部類別的public方法來存 / 取內部類別: 在上面的例子中,我可以不使用內部類別,像往常一樣分別定義兩個(外部)類別Human和Wearing就好了呀,幹嘛還要多此一舉從外部類別來存取內部類別?所以內部類別到底有什麼作用?其實使用內部類別,最大的原因在於可以提高程式的安全性、穩定性,還有程式的結構性。 以上面的程式為例,我既然將內部類別Wearing包裹在外部類別Human之內,為什麼還要讓別人可以存取內部類別呢?我建立內部類別的主要目的,就是要防止它被外界任意存取。用比喻來說,例如引擎是個非常專業的東西,引擎內部還有很多內部類別,但我希望只有原廠的專業技師能拆它,不讓別人隨便拆,不然很可能會壞掉或是裝不回去。 假設我的外部類別Human裡面有個屬性wear是內部類別Wearing的物件,我只開放某些方法作為窗口,別人只能透過這些方法存取物件wear的值,而無法直接存取內部類別,或利用內部類別創造物件: class Human { // 外部類別Human開始 private String name; // 屬性name設為private,不讓別人直接存取 private Wearing wear; // wear是內部類別Wearing型態的物件,是Human的屬性之一 public Human(String str) { // 定義外部類別的建構子,權限為public this.name = str; wear = new Wearing(); // 由於wear是物件,還是需要利用new來做初始化 } private class Wearing { // 將內部類別的權限設為private String color; // 定義內部類別的屬性color char size; // 定義內部類別的屬性size } public void setWear(String c, char s){ // 開放此方法,別人只能透過這個方法設定wear wear.color = c; wear.size = s; } public String getWear() { // 開放此方法,別人只能透過此方法取得wear的內容 return name +" 穿的服裝顏色是:" + wear.color + " 尺寸是:" + wear.size ; } } // 外部類別Human結束 我們把內部類別的權限設為private,並且只開放兩個「窗口」讓別人透過外部類別來存取內部類別。 之後,我們宣告一個測試用的類別Test.java,看看怎麼使用外部類別Human來存取內部類別Wearing: class Test { // 宣告測試用的類別Test.java public static void main(String[ ] args) { Human tina = new Human("小婷"); // 使用外部類別創造物件 tina.setWear("white", 's'); // 透過外部類別的public方法存取內部類別的private屬性 System.out.println(tina.getWear()); // 同上 } // 主程式main(){ }結束 } // Test類別結束 執行結果: 小婷的服裝顏色:white,尺寸:s 這樣設計的好處是,能夠防止別人亂用你的內部類別,還可以確保內部類別的屬性和方法被正確的使用。例如呼叫外部類別的setWear()方法,可以確保設定wear的時候一定會帶入兩個引數,當然也可以做進一步引數的檢查、轉換,而getWear()方法也是同理。 這就是物件導向「封裝(Encapsulation)」的精神,可以保護程式的安全性和穩定性,同時又不犧牲完整性。 雖然內部類別有封裝性的好處,但如前所述,在同一個內部類別中,不建議再撰寫超過兩層(或三層)以上的內部類別。雖然對電腦來說沒差,但對於人類來說,會降低程式的可讀性,讓程式變得很複雜,提高出錯的機率。