2016-08-30 79 views
-3
public class A { 
    public A() { 
     System.out.println("A()"); 
    } 

    public class B { 
     public B() { 
      System.out.println("B()"); 
     } 
    } 
} 
class Caller extends A.B { 
    Caller(A a){ 
     a.super(); 
    } 
} 


public class Main { 
    public static void main(String[] args) { 
     Caller as= new Caller(new A()); 
    } 
} 

爲什麼我們需要在類中調用a.super()擴展內部類?它在做什麼?繼承自內部類

沒有a.super()程序不想編譯。

Error:(48, 20) java: an enclosing instance that contains A.B is required 
+4

擴展封閉類之外的*內*類是一個非常** **糟糕的代碼設計。你應該重新考慮設計。 – Andreas

+0

編輯問題時不提及編輯問題本身也不太好。 – Scadge

+0

[此編輯](http://stackoverflow.com/revisions/39231197/5)的目的是添加一個不完整的'C'聲明?它對這個問題毫無影響。 –

回答

3

答案是:因爲這是在Java語言規範中指定的方式。

您的班級A.BA的內部班級。構造函數具有類型爲A的隱藏參數 - 封閉(外部類)實例。

您已在您的班級Caller中劃分了A.B,這本身並不是內部類。但是超類的構造函數需要這個隱藏的實例A - 外部類實例。

您在Java中傳遞此方法的方式是使用此a.super();語法。

Java語言規範定義這在section 8.8.7.1

合格超類構造調用與主 表達ExpressionName開始。它們允許一個子類的構造函數 明確指定新創建的對象的直接超類(§8.1.3)的直接封裝 實例。當超類是內部類時,這可能是 。

-1

由於您Caller類擴展A.B,首先調用Caller構造品牌將是A.B構造函數(即使你沒有看到它在你的代碼)。因此a.super()不調用A.B構造函數。

+0

爲什麼a.super()什麼都不做? –

+0

沒有'a.super'程序不想編譯。 –

+1

因爲原來的問題改變總是很有趣,所以得到downvoted吧@Scadge? – Adam

-2

這不是a.super()構造函數調用A.B,而是Caller()構造函數隱式調用它,因爲Caller extends A.B。只有在執行a.super()行之後。

+0

不是。 'Caller'擴展了'A.B',而不是'A',所以它的構造函數調用'A.B'的構造函數,而不是'A'。 'A.B'擴展了Object,所以它的構造函數不會調用A的構造函數。 'a.super()'實際上* does *調用'A.B'的構造函數。 –

+0

@JohnBollinger我並不是說'Caller()'調用''A的構造函數,請再次閱讀我的答案。另外,最初的問題是「爲什麼'a.super()'調用'A.B'構造函數」,我的答案解釋說它沒有。 – Scadge

+2

@Scadge但它*確實*,這就是爲什麼你的答案是錯誤的。亞當羅西尼的回答也是。 –

5

a.super()不是調用A構造函數。構造函數A作爲表達式new A()的結果在Main.main()中運行。

a.super()調用零元(只)B構造,指定a爲的A含實例,其中,因爲內部類A.B的一個子類中,每個Caller必須有一個參考。

+0

爲什麼'a.super()'調用nullary'B'構造函數? 對象'a'是'A'的一個實例,而不是'B'!它如何確定構造函數需要調用? –

+1

@NickNick:因爲這就是這種奇怪的語法是如何工作的。 'a.super()'與'obj.method()'有着根本的區別。其中'obj.method()'表示在obj上調用''方法'','a.super()'意味着「調用正在構造的這個實例的超類構造函數,提供'a'作爲外部類的引用。 (*應該*他們已經使用'x.y'語法?上帝不,但他們做到了。) –

0

錯誤說明了一切。內部類(非靜態嵌套類)的關鍵之一是它們可以訪問其外部(封閉)實例。但是內部類需要知道它屬於哪個外部實例。這些信息保存在我們無法訪問的Outer this$0引用中,但它仍然需要設置爲某一點,並且這一點是構造函數的代碼。

這就是爲什麼我們要通過創建outer.new Inner()內部類實例像

Outer outerInstance= new Outer(); 
Outer.Inner innerInstance = outerInstance.new Outer.Inner(); 
//       ^^^^^^^^^^^^^^^^^ 

感謝的是,外實例也傳遞給內部類的構造函數(如隱藏參數)。

現在既然要擴展內部類,這意味着你的類只需指定內部類,但這並不意味着它不再是Inner類型。

因此,因爲Caller的實例也被認爲是A.B內部類(因爲它擴展了它),所以您必須確保它具有關於其外部實例(A類)的知識。要做到這一點,你在我們班的構造函數,你需要

  1. 有你的類的實例內超A.B
  2. 調用構造函數,所以你會讓它保存this$0變量外部類的參考。

第一點是通過傳遞一個實例作爲您的類構造函數的參數Caller(A a)來解決的。

第二點與調用outerInstance.new Inner()的方法類似,但這次我們不能使用new關鍵字,因爲我們不想創建Inner類的新對象。我們想從超類構造函數調用代碼來初始化當前的對象(及其隱藏的this$0字段正確)。通常的做法是致電super()。唯一可能有點奇怪的是,我們需要以某種方式讓super()調用哪個外部實例包含一個實例。所以,很可能使其類似於outer.new Inner()語法,我們使用outer.super()語法,你的情況是

a.super();