Java 四大訪問修飾符 (public、protected、default、private) 看似簡單,但結合**內部類**、**跨包繼承**、**構造器調用**時極易產生反直覺的編譯錯誤。本文通過一個經典案例,揭示 protected 內部類構造器的可見性規則。
// 父類包 pac1
package pac1;
public class Parent {
protected int i;
protected class Inner { } // protected 成員內部類
}
// 子類包 pac2
package pac2;
import pac1.Parent;
public class Child extends Parent {
public static void main(String[] args) {
new Child().i = 0; // ✅ 編譯通過
Inner i = new Parent().new Inner(); // ❌ 編譯失敗
}
}
Error: Inner() has protected access in pac1.Parent.Inner
Parent.Inner 為 protected,子類 Child 繼承 Parent,**可見類類型**,故 Inner i 聲明合法。new Parent().new Inner() 調用的是 Inner 的**無參構造器**。protected。protected 構造器**僅對同包類及子類可見**,但**子類必須通過繼承鏈訪問**,而非通過外部類實例訪問。Child 雖繼承 Parent,但 new Parent() 創建的是 **父類實例**,而非子類實例。在 Parent 實例上調用 Inner() 視為**外部類代碼訪問**,非繼承訪問 → **不可見**。A protected constructor can be accessed by a class in the same package, or by a subclass of its declaring class... **but only when the access occurs within the subclass itself** (i.e., the qualifying type of the constructor invocation is the subclass or a subclass thereof).
譯文:protected 構造器僅可在**子類自身代碼中**通過 super()、this() 或 new Child.Inner() (若 Inner 為 Child 成員) 訪問,**不能**通過父類實例 new Parent().new Inner() 訪問。
// Parent.java
package pac1;
public class Parent {
protected class Inner { }
// 提供 protected 工廠方法,子類可繼承調用
protected Inner createInner() { return new Inner(); }
}
// Child.java
package pac2;
public class Child extends Parent {
public static void main(String[] args) {
Child child = new Child();
Parent.Inner inner = child.createInner(); // ✅ 子類實例調用繼承的 protected 方法
}
}
public class Child extends Parent {
public static void main(String[] args) {
Child child = new Child();
Parent.Inner inner = child.new Inner(); // ✅ 在 Child 實例上創建 Inner,合法
// 原理:Child 繼承了 Parent,故 Child 實例也是 Parent 實例,滿足外部類實例要求
}
}
public class Parent {
protected class Inner {
public Inner() { } // 顯式 public 構造器
}
}
// 此時 new Parent().new Inner() 合法,但破壞了封裝,慎用
| 場景 | protected 類/構造器 | 子類跨包訪問 | 關鍵點 |
|---|---|---|---|
| 聲明變量類型 | 類 | ✅ 允許 | 類型可見性按類級別檢查 |
| new Outer().new Inner() | 構造器 | ❌ 禁止 | 外部類實例非子類實例,視為外部訪問 |
| new Child().new Inner() | 構造器 | ✅ 允許 | Child 實例滿足「在子類代碼中」+「合法外部實例」 |
| super() / this() | 構造器 | ✅ 允許 | 構造器鏈調用屬於子類內部 |
| 繼承的 protected 方法創建 | 方法 | ✅ 允許 | 方法在子類中調用,屬於子類代碼 |
protected。protected Inner createInner(),子類復用/覆寫,封裝創建邏輯。static class Inner 完全避開此問題。