2010-05-14 67 views
15

我很難包裝我的頭在Java中的非靜態嵌套類。考慮下面的例子,它打印「Inner」和「Child」。Java:非靜態嵌套類和instance.super()

class Outer { 
    class Inner { 
     Inner() { System.out.println("Inner"); } 
    } 
} 

public class Child extends Outer.Inner { 
    Child(Outer o) { 
     o.super(); 
     System.out.println("Child"); 
    } 
    public static void main(String args[]) { 
     new Child(new Outer()); 
    } 
} 

我知道Inner的實例總是必須與一個Outer實例關聯,並且它也適用於Child,因爲它擴展了Inner。我的問題是o.super()語法的含義 - 爲什麼它會調用Inner構造函數?

我只看到用來調用父類的構造純super(args)super.method()調用覆蓋方法的超類版本,但從未形式instance.super()的東西。

+0

大聲笑...這是什麼'面試 - 問題'標籤? – Cristian 2010-05-14 04:26:41

+0

作爲Java職位面試的一部分,我被要求完成IKM測驗;這個問題是測驗中的一個簡化形式。 – Kiv 2010-05-14 12:34:09

+0

@Kiv你有沒有現實生活中的例子? – chepseskaf 2013-10-07 13:14:10

回答

9

它被稱爲「合格的超類構造函數調用」。

here舉例:

顯式構造函數調用語句可以分爲兩種:

  • 可選的構造調用開始與這個關鍵字(可能顯式類型參數的開頭)。它們用於調用同一個類的替代構造函數。

  • 超類構造函數調用以super關鍵字(可能以顯式類型參數開頭)或主表達式開始。它們用於調用直接超類的構造函數。超類構造函數調用可以進一步細分:

  • 不合格的超類構造函數的調用與關鍵字super(可能帶有明確的類型參數的開頭)開始。

  • 合格的超類構造函數調用以一個主表達式開始。它們允許子類構造函數明確指定新創建的對象相對於直接超類的直接封閉實例(第8.1.3節)。當超類是內部類時,這可能是必要的。

9

內部類(非靜態的子類)基本上嵌套類(靜態子類)與隱性鏈接回到他們的父對象。這是你上面的代碼,而不是使用一個靜態嵌套類寫着:

class Outer { 
    static class Inner { 
     final Outer outer; 
     Inner(Outer outer) { 
      this.outer = outer; 
      System.out.println("Inner"); 
     } 
    } 
} 

public class Child extends Outer.Inner { 
    Child(Outer o) { 
     super(o); // o.super(); 
     System.out.println("Child"); 
    } 

    public static void main(String args[]) { 
     new Child(new Outer()); 
    } 
}

看着這個,你應該能夠理解o.super()在做什麼。

+2

事實上,根據我的理解,非靜態內部類本質上是上述的語法糖。 – 2010-05-14 02:42:48

+0

不,嵌套的靜態類本質上是一個外層的類,這只是爲了方便打包。 – 2010-05-14 02:51:12

+1

好吧,對。我的意思是一個*非靜態的*內部類是語法糖頂部的語法糖:-D – 2010-05-14 02:55:21

2

從概念上講,非靜態內部類「屬於」特定對象。它就像每個人都有自己的類的版本一樣,就像屬於特定對象的非靜態字段或方法一樣。

所以這就是爲什麼我們有有趣的語法像instance.new Inner()instance.super() - 爲背景,其中問題的答案「但其Inner?」是不是很明顯。 (在外部類的非靜態方法,你就可以說new Inner(),和往常一樣這對於短this.new Inner()

5

爲什麼o.super()Child最終調用Outer.Inner構造?這很簡單:因爲Child extends Outer.Inner和構造函數調用總是被鏈接在層次結構中。

這裏有輕微擴張到您的片段來說明:

class Outer { 
    Outer() { 
     System.out.println("Outer"); 
    } 
    void outerMethod() { } 
    class Inner { 
     Inner() { 
      System.out.println("OuterInner"); 
      outerMethod();    
     } 
     String wealth; 
    } 
} 
class OuterChild extends Outer { 
    OuterChild() { 
     System.out.println("OuterChild"); 
    } 
} 
public class OuterInnerChild extends Outer.Inner { 
    OuterInnerChild(Outer o) { 
     o.super(); 
     System.out.println("OuterInnerChild"); 
     this.wealth = "ONE MILLION DOLLAR!!!"; 
    } 
    public static void main(String args[]) { 
     System.out.println(new OuterInnerChild(new Outer()).wealth); 
     new OuterChild(); 
    } 
} 

此打印:

Outer 
OuterInner 
OuterInnerChild 
ONE MILLION DOLLAR!!! 
Outer 
OuterChild 

一些主要意見:

  • 因爲OuterInnerChild extends Outer.Inner,它繼承wealth,就像正常的子類語義
    • 而且就像正常的子類語義的OuterInnerChild鏈構造函數的Outer.Inner
  • 因爲OuterChild extends Outer構造,它的構造函數鏈,即使明確地不援引
    • 無論是或明或暗地中,構造函數鏈向上層次

但爲什麼編譯器的需求是OuterInnerChild構造函數採用Outer o,並o.super()被調用?

現在專用於內部類的語義:它的工作要做,以確保OuterInnerChild所有實例已經一個封閉Outer實例Outer.Inner,超階級的OuterInnerChild。否則,Outer.Inner的構造函數將不會有一個封閉實例Outer來調用outerMethod()

0

總是不要忘記基本原則,在調用子類構造函數的過程中,它總是首先實例化父類,而不考慮內部/外部類。在你的場景中,當你擴展內部類時,你的內部類是需要實例化的父類的成員,然後調用實際的內部類構造函數。