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

Different member behaviour of inner class if inner class extends outer class?

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

如果我有以下 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 扩展外部 class 这不起作用。

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

我觉得这很奇怪...有人可以解释为什么会有这种差异吗?

val in InheritedB 是指来自其 base class (super.val) 的 val,因为那是 this.

的一部分

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

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


If I have a look at the constructors I also see that a new instance of B will be created if instance of InheritedB is created.

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

这一行:

new InheritedB().printVal();   

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

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

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

InheritedB中的这条语句:

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

打印出继承自Bval的值,后面是"containing instance".

中的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();

希望您能准确了解那里发生的事情。编译器 粗略地 将您的原始代码执行到该代码中。

因为 InheritedB extends B,创建 InheritedB 的实例会授予它一个 val 属性,对于任何 new B,默认情况下是 "old" class 或子class 实例。

这里,InheritedB 打印 它自己的 val 属性,而不是封闭的 B 实例之一。

InheritedB 的情况下,有两个变量称为 val,一个是 B,另一个是 InheritedB。应用可见性规则给出观察到的结果。

区别是class InnerB里面没有成员val。其中 class InheritedB 扩展 class 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的副本仍然是“old”值,没有修改,printVal函数使用那个值。