2016-01-04 81 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函数使用该值。