提到 Java 內部類,許多開發者可能感到陌生。其實這概念在 C++ 中對應「嵌套類」,兩者雖有相似卻存在關鍵差異。內部類不僅是「類中定義類」這麼簡單,更是 Java 設計者精心打造的進階特性。掌握它能讓程式結構更優雅、封裝性更強。本文將從基本語法、訪問特性、靜態與非靜態差異、局部與匿名內部類、以及解決多重繼承限制等方面深入剖析。
以下範例展示外部類 Goods 內部定義了 Content 與 GDestination 兩個內部類,分別實現 Contents、Destination 介面:
public interface Contents { int value(); }
public interface Destination { String readLabel(); }
public class Goods {
private class Content implements Contents {
private int i = 11;
public int value() { return i; }
}
protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) { label = whereTo; }
public String readLabel() { return label; }
}
public Destination dest(String s) { return new GDestination(s); }
public Contents cont() { return new Content(); }
}
class TestGoods {
public static void main(String[] args) {
Goods p = new Goods();
Contents c = p.cont();
Destination d = p.dest("Beijing");
}
}
外部呼叫端僅見到介面型別 Contents、Destination,完全不知內部類名稱,完美實現封裝與隱藏。外部類透過方法回傳內部類實例,是獲取非靜態內部類物件的常見方式。
非靜態內部類物件隱含一個指向外部類物件的引用,因此能存取外部類所有成員(含 private)。編譯器會在建構子隱式傳入外部引用,這也是為何在外部建立內部類物件時,必須先有外部類物件:
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
範例中 Content.value() 直接使用外部類 valueRate,驗證了此特性。若內外部類有同名欄位,可用 OuterClass.this.field 明確指向外部成員。
可在方法、if 區塊等作用域內定義類,作用域結束後無法在外部實例化,但編譯期仍會產生 .class 檔。適合一次性、區域性的邏輯封裝。
public class Goods1 {
public Destination dest(String s) {
class GDestination implements Destination { ... }
return new GDestination(s);
}
}
語法:new InterfaceName() { ... }; 或 new SuperClassName() { ... };。常用於事件監聽器、執行緒建立等單次實作場景。注意:無建構子,需用初始化區塊或外部 final 參數初始化欄位。
public class Goods3 {
public Contents cont() {
return new Contents() {
private int i = 11;
public int value() { return i; }
};
}
}
// 事件處理範例
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
若一類需實作多個介面,且介面方法簽名衝突,可用內部類分別實作,再由外部類委派。內部類可存取外部所有成員,等效於多重繼承的狀態共享。這是 Java 以「內部類 + 介面」優雅替代 C++ 複雜多重繼承的核心設計動機。