【问题标题】:Different member behaviour of inner class if inner class extends outer class?如果内部类扩展外部类,内部类的不同成员行为?
【发布时间】:2016-04-08 04:27:36
【问题描述】:

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

如果我有以下课程...

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!

如果我查看构造函数,我还发现如果创建了 InheritdB 的实例,则会创建一个新的 B 实例。

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

【问题讨论】:

  • 你确定你说的不是InheritedB extends InnerB
  • 为了我的安心,请告诉我您不打算在实际代码中执行此操作,您只是想看看发生了什么。

标签: java inheritance inner-classes


【解决方案1】:

InheritedB 中的val 指的是来自其基类 (super.val) 的val,因为这是this 的一部分。

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

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


如果我查看构造函数,我还发现如果创建了 InheritdB 的实例,则会创建一个新的 B 实例。

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

【讨论】:

    【解决方案2】:

    这一行:

    new InheritedB().printVal();   
    

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

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

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

    InheritedB中的这句话:

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

    打印出继承自Bval 的值,然后是“包含实例”中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();
    

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

    【讨论】:

      【解决方案3】:

      由于 InheritedB extends B,创建 InheritDB 的实例会为其授予 val 属性,对于任何 new B 类或子类实例,该属性默认为“旧”。

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          区别在于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

          但是在 InheritdB 的对象中 val 的副本仍然是“old”值,没有被修改,printVal 函数使用该值。

          【讨论】:

            猜你喜欢
            • 2013-10-21
            • 1970-01-01
            • 2012-06-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-03-16
            • 1970-01-01
            • 2015-09-28
            相关资源
            最近更新 更多