如果内部类扩展外部类,内部类的不同成员行为?

时间:2016-01-04 14:18:40

标签: java inheritance inner-classes

今天我偶然发现了一些奇怪的内部(非静态)类行为。

如果我有以下课程......

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实例。

我发现这很奇怪......有人能解释为什么会有这种差异吗?

5 个答案:

答案 0 :(得分:26)

这一行:

new InheritedB().printVal();   

创建一个InheritedB的新实例,其实例是B的现有实例(其中val为"new")。但此时有两个 val变量:

  • 现有B
  • 实例中的一个
  • InheritedB实例中的一个,具有单独的val字段

第二个变量的值为"old",因为它实际上是该字段的默认值。

InheritedB中的此声明:

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

打印出从val继承的B的值,后跟“包含实例”中的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();

希望你能完全按照那里发生的事情。编译器大致将原始代码执行到该代码中。

答案 1 :(得分:9)

val中的{p> InheritedB引用其基类val)中的super.val,因为它是this的一部分{ {1}}。

如果您不从外部类继承,val引用外部类(B.this.scope)的范围。但是,由于您继承,this的范围更近,因此隐藏了外部范围。

由于您从未在内部run() 上调用this this.val仍为old

  

如果我查看构造函数,我还会看到如果创建了InheritedB的实例,将创建一个新的B实例。

是;创建派生类将始终创建其基类的实例。无法从现有实例继承。

答案 2 :(得分:5)

InheritedB extends B开始,创建一个InheritedB实例会授予它val属性,默认情况下,该属性对于任何 B类或子类实例都是“旧”。

此处,InheritedB打印自己的 val属性,而不是打包的B实例。

答案 3 :(得分:4)

InheritedB的情况下,有两个名为val的变量,BInheritedB之一。应用可见性规则可以得出观察结果。

答案 4 :(得分: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方法中修改为值 new

但是InheritedB的对象中的val副本仍然是“”值,未修改,printVal函数使用该值。