【问题标题】:Accessing outer class members from within an inner class extending the outer class itself从扩展外部类本身的内部类中访问外部类成员
【发布时间】:2013-10-21 09:05:02
【问题描述】:

在下面显示的代码sn-p中,内部类继承了外部类本身。

package test;

class TestInnerClass {

    private String value;

    public TestInnerClass(String value) {
        this.value = value;
    }

    private String getValue() {
        return value;
    }

    public void callShowValue() {
        new InnerClass("Another value").showValue();
    }

    private final class InnerClass extends TestInnerClass {

        public InnerClass(String value) {
            super(value);
        }

        public void showValue() {
            System.out.println(getValue());
            System.out.println(value);
        }
    }
}

public final class Test {

    public static void main(String[] args) {
        new TestInnerClass("Initial value").callShowValue();
    }
}

main() 方法内的唯一语句(最后一个 sn-p)将值 Initial value 分配给 TestInnerClass 类的私有字段 value,然后调用 callShowValue() 方法。

callShowValue() 方法导致另一个字符串 - Another value 在调用 InnerClass 扩展 TestInnerClassshowValue() 方法之前设置为 TestInnerClass 类的私有字段 value

相应地,showValue()方法里面的下面两条语句,

System.out.println(getValue());
System.out.println(value);

应该显示,

另一个价值
另一个价值

但它们显示,

初始值
初始值

为什么会这样?

【问题讨论】:

    标签: java inheritance inner-classes


    【解决方案1】:

    方法getValue()和字段value都是private。因此,任何其他类(包括子类)都无法访问它们。它们不是遗传的。

    InnerClass#showValue()

    public void showValue() {
        System.out.println(getValue());
        System.out.println(value);
    }
    

    因为它们是私有的,getValue()value 指的是外部类的成员,它们是可以访问的,因为你在同一个类中,即。 内部类可以访问外部类私有成员。以上调用等价于

    public void showValue() {
        System.out.println(TestInnerClass.this.getValue());
        System.out.println(TestInnerClass.this.value);
    }
    

    既然您已将value 设置为

    new TestInnerClass("Initial value")
    

    您看到"Initial value" 被打印了两次。 无法访问子类中的那些private 成员。


    重点是:不要让子类成为内部类。

    【讨论】:

      【解决方案2】:

      在上述情况下,TestInnerClass 和 InnerClass 之间有 2 种不同的关系。

      但故事有一点转折.. 有两个对象!问题是我们将 "Another Value" 放入不同的对象中!并打印旧的 ..

      第一个对象:

      public static void main(String[] args) {
          new TestInnerClass("Initial value").callShowValue();
      }
      

      Test Class 中的Above 方法使用“初始值”

      创建一个TestInnerClass 实例

      第二个对象:

      public void callShowValue() {
          new InnerClass("Another value").showValue();
      }
      

      由于 InnerClass 正在扩展 TestInnerClass,因此使用 “另一个值” 创建了另一个新的 TestInnerClass 实例。 但是我们打印的是旧对象的值,而不是第二个。

      【讨论】:

        【解决方案3】:

        这里的关键是了解内部类如何访问外部类的成员。在privatenon-private 成员的情况下,如何访问这些成员。 (注意:我将在这里讨论非static 内部类,因为问题仅与此有关)。

        内部类存储对封闭实例的引用:

        内部类将对封闭实例的引用存储为字段。该字段名为this$0。封闭实例总是绑定到内部类对象。从封闭类内部创建内部类对象时,this$0 的引用值对所有对象都保持不变,但this 的引用会有所不同。

        您在内部类中使用Outer.this 语法访问this$0 字段。例如,考虑以下代码:

        class Outer {
            public Outer() { }
        
            public void createInnerInstance() {
                Inner obj1 = new Inner();
                Inner obj2 = new Inner();
            }
        
            private class Inner {
                public Inner() {
                    System.out.println(Outer.this);
                    System.out.println(this);
                }
            }
        }
        
        public static void main(String[] args) {
            new Outer().createInnerInstance();
        }
        

        当你执行这段代码时,你会得到这样的输出:

        Outer@135fbaa4
        Outer$Inner@45ee12a7
        Outer@135fbaa4
        Outer$Inner@330bedb4
        

        注意 1st 和 3rd 引用是相同的,而 2nd 和 4th 是不同的.


        外部类成员可以使用this$0参考在内部类中访问:

        当您从内部类访问字段或外部类的任何其他成员时,访问表达式会自动使用this$0 限定。您使用OuterClass.this 引用明确地将成员访问限定为this$0。因此,考虑外部类中的字段valuepublic,然后在内部类中的showValue() 方法中:

        public void showValue() {
            System.out.println(TestInnerClass.this.value);
            System.out.println(value);
        }
        

        前 2 个打印语句是等效的。它们将被编译成相同的字节码:

         public void showValue();
           Code:
              0: getstatic     #3                  // Field java/lang/System.out:Ljava/
        o/PrintStream;
              3: aload_0
              4: getfield      #1                  // Field this$0:LTestInnerClass;
              7: getfield      #4                  // Field TestInnerClass.value:Ljava/lang/Stri
        g;
             10: invokevirtual #5                  // Method java/io/PrintStream.printl
        :(Ljava/lang/String;)V
             13: getstatic     #3                  // Field java/lang/System.out:Ljava/
        o/PrintStream;
             16: aload_0
             17: getfield      #1                  // Field this$0:LTestInnerClass;
             20: getfield      #4                  // Field TestInnerClass.value:Ljava/lang/Stri
        g;
             23: invokevirtual #5                  // Method java/io/PrintStream.printl
        :(Ljava/lang/String;)V
             26: return
        

        您不能使用this 显式访问外部类成员:

        如果您在内部类中显式尝试使用this 限定字段或方法访问表达式,您将收到编译器错误:

        public void showValue() {
            System.out.println(this.value);  // this won't compile
        }
        

        上面的 print 语句不会编译,因为value 不是内部类本身的字段。这是一个外部类字段。 this 指的是内部类实例,而不是外部类实例。


        内部类扩展外部类时故事发生变化:

        当你的内部类扩展了外部类时,事情就开始变得奇怪了。因为在这种情况下,使用this 限定字段或方法访问将对非私人成员有效。对于private 成员,这仍然无效,因为private 成员不会被继承。

        在继承内部类的情况下,直接访问外部类成员用this 限定。这意味着,它们将作为内部类成员访问。虽然使用 Outer.this 明确限定访问权限将引用封闭实例的字段 - this$0

        考虑到value 字段被声明为public

        public void showValue() { 
            System.out.println(value);            // inner class instance field
            System.out.println(this.value);       // inner class instance field
            System.out.println(Outer.this.value); // enclosing instance field
        }
        

        前两条打印语句将打印内部类实例的value 字段,而第三条打印语句将打印封闭实例的value 字段。困惑?

        记得我说过,当你从外部类内部创建多个内部类实例时,它们将具有相同的this$0 引用。

        假设你创建了一个外部类实例:

        new Outer("rohit").callShowValue();
        

        然后在callShowValue() 方法中,创建内部类的实例:

        new Inner("rj").showValue();
        

        现在,showValue() 方法的输出将是:

        rj
        rj
        rohit
        

        您会注意到,this.valueOuter.this.value 不同。

        如果你把value字段设为private

        现在,当您创建外部类字段private 时,当然您不能使用this.value; 访问它。所以,第二个打印语句不会编译。

        在这种情况下直接访问字段将被限定为this$0。现在更改字段value private,并将showValue()方法修改为:

        public void showValue() { 
            System.out.println(value);            // enclosing instance field
            System.out.println(this.value);       // compiler error
            System.out.println(Outer.this.value); // enclosing instance field
        }
        

        这就是问题所在。根据字段是public 还是private,第一个打印语句将value 限定为thisthis$0


        解决您的具体问题:

        现在在您的代码中,因为value 字段和getValue() 方法都是privateshowValue() 方法:

        public void showValue() {
            System.out.println(getValue());
            System.out.println(value);
        }
        

        等同于:

        public void showValue() {
            System.out.println(TestInnerClass.this.getValue());
            System.out.println(TestInnerClass.this.value);
        }
        

        这是访问封闭实例的字段和方法。并且该字段仍然是初始值。这就是为什么输出是:

        初始值
        初始值

        【讨论】:

        • 该死的,伙计。在相关说明中,我也将开始在我的答案中使用我的名字。 :)
        【解决方案4】:

        其他答案已经很好地解释了为什么你会得到你看到的结果(你有两个 TestInnerClass 实例并且正在访问第一个),但是实际上有一种方法可以访问 TestInnerClass 的私有成员在它的子类 - 关键字super

        如果您将 showValue 方法替换为:

        public void showValue() {
            System.out.println(super.getValue());
            System.out.println(super.value);
        }
        

        你会得到你期望的输出。

        我还建议,如果您决定这样做,请将 InnerClass 设为静态内部类,因为它不再需要对其外部类实例的引用。

        【讨论】:

          猜你喜欢
          • 2018-06-26
          • 2015-09-28
          • 1970-01-01
          • 2016-04-08
          • 2019-09-03
          • 2011-08-11
          • 1970-01-01
          • 1970-01-01
          • 2012-10-29
          相关资源
          最近更新 更多