从扩展外部类本身的内部类中访问外部类成员

时间:2013-10-13 19:36:48

标签: java inheritance inner-classes

在下面显示的代码片段中,内部类继承外部类本身。

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()方法(最后一个代码段)中的唯一语句将值Initial value分配给value类的私有字段TestInnerClass,然后调用{{ 1}}方法。

callShowValue()方法导致另一个字符串 - callShowValue()被设置为Another value类的私有字段value,然后再调用TestInnerClass方法showValue()延长InnerClass

因此,TestInnerClass方法中的以下两个语句

showValue()

应该显示,

  

另一个值
  另一个值

但他们显示,

  

初始值
  初始值

为什么会这样?

4 个答案:

答案 0 :(得分:8)

方法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成员。


关键是:不要让子类成为内部类。

答案 1 :(得分:8)

这里的关键是理解内部类如何访问外部类的成员。如果privatenon-private成员,访问这些成员的方式如何。 (注意:我会在这里讨论非static内部类,因为问题仅限于此。)

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

内部类将对封闭实例的引用存储为字段。该字段命名为this$0。封闭实例始终绑定到内部类对象。当您从封闭类中创建内部类的对象时,this$0的引用值对于所有这些对象保持相同,但this引用会有所不同。

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

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

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


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

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

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

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

 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
}

前两个print语句将打印内部类实例的value字段,而第三个print语句将打印封闭实例的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;访问它。因此,第二个print语句将无法编译。

此次直接访问该字段将获得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
}

这就是问题所在。根据字段是value还是this,第一个打印语句使this$0public符合private


来你的具体问题:

现在在您的代码中,由于value字段和getValue()方法均为private,因此showValue()方法:

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);
}

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

  

初始值
  初始值

答案 2 :(得分:2)

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

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

第一个对象:

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

Test Class中的上述方法使用“初始值”创建一个TestInnerClass实例

第二个对象:

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

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

答案 3 :(得分:1)

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

如果您将super方法替换为:

showValue

您将获得预期的输出。

我还建议如果你决定这样做,你可以使public void showValue() { System.out.println(super.getValue()); System.out.println(super.value); } 成为一个静态内部类,因为它不再需要引用它的外部类的实例。