2016-01-04 119 views
24

今天我偶然發現了一些奇怪的內部(非靜態)類行爲。如果內部類擴展外部類,內部類的不同成員行爲?

如果我有以下類...

class B { 
    String val = "old"; 

    void run(){ 
     val = "new"; 
     System.out.println(val);  // outputs: new 
     new InnerB().printVal();  // outputs: new 
    } 

    private class InnerB { 
     void printVal(){ System.out.println(val); } 
    } 
} 

new B().run(); 

...一切似乎是明確的。 InnerB的實例屬於B的實例,所以如果它應該輸出val,它會打印已經替換的值'new'。

但如果內部類擴展外部類,這是行不通的。

class B { 
    String val = "old"; 

    void run(){ 
     val = "new"; 
     System.out.println(val);  // outputs: new 
     new InnerB().printVal();  // outputs: new 
     new InheritedB().printVal(); // outputs: old new 
    } 

    private class InnerB { 
     void printVal(){ System.out.println(val); } 
    } 

    private class InheritedB extends B{ 
     void printVal(){ System.out.println(val + " "+ B.this.val); } 
    } 
} 

new B().run(); // outputs: new new old! 

如果我有一個看看構造我也看到,如果創建InheritedB實例B的新實例將被創建。

我覺得這很奇怪......有人可以解釋爲什麼有這種差異?

+0

你確定你不是指'InheritedB extends InnerB'嗎? – user3707125

+0

爲了讓我安心,請告訴我你不打算在真正的代碼中這樣做,你只是在亂搞,看看發生了什麼。 – jpmc26

回答

26

這條線:

new InheritedB().printVal(); 

創建的InheritedB一個新實例,其含有實例是B現有實例(其中,val爲"new")。但在這一點上有 val變量:

  • 的一個在B
  • 的一個在InheritedB實例,其中有一個單獨val

的現有實例第二個變量的值是"old",因爲這實際上是字段的默認值。

這種說法在InheritedB

System.out.println(val + " "+ B.this.val); 

打印出的valB繼承了值,其次是val在「含實例」的價值。

這可能是簡單的認爲它被重構爲:

public class B 
{ 
    String val = "old"; 
} 

public class InheritedB extends B { 
    B other; 

    public InheritedB(B other) 
    { 
     this.other = other; 
    } 

    void printVal() { 
     System.out.println(val + " "+ other.val); 
    } 
} 

那麼你基本上運行:

B original = new B(); 
original.val = "new": 
InheritedB inherited = new InheritedB(original); 
inherited.printVal(); 

希望你可以按照到底發生了什麼在那裏。編譯器是大致執行您的原代碼到該代碼。

9

InheritedBvalval從其基類super.val),因爲這是的this一部分。

如果您沒有從外部類繼承,val引用範圍從外部類(B.this.scope)。但是,由於您繼承,因此this的範圍更接近,因此隱藏了外部範圍。

因爲你從來沒有在內部this稱爲run()this.val仍然是old


如果我有一個看看構造我也看到,如果創建InheritedB實例B的新實例將被創建。

是;創建派生類將始終創建其基類的一個實例。沒有辦法從現有的實例繼承。

5

由於InheritedB extends B,創建InheritedB的實例授予它val屬性,它是「舊」預設爲任何乙類或子類的實例。

這裏,InheritedB打印自己的val屬性,封閉乙實例不是一個。

4

InheritedB的情況下,有兩個變量0123',BInheritedB之一。應用可見性規則給出觀察結果。

2

區別在於類InnerB沒有會員val在裏面。類別InheritedB擴展B類,並擁有val成員的自己副本。

void run(){ 

    val = "new";  //<--- modifies B's val not InheritedB's val 

    System.out.println(val);  // outputs: new 
    new InnerB().printVal();  // outputs: new 
    new InheritedB().printVal(); // outputs: old new 
} 

在上面的代碼塊,InnerB的printVal訪問容器的val構件,其值在run方法已經修改爲值

但是,InheritedB對象中val的副本仍然是「舊的」值,未修改,並且printVal函數使用該值。