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

Tin*_*iny 9 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
public final class Test {

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

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

callShowValue()方法导致另一个字符串 - 在调用扩展方法之前Another value设置valueTestInnerClass该类的私有字段.showValue()InnerClassTestInnerClass

因此,showValue()方法内部有以下两个语句,

System.out.println(getValue());
System.out.println(value);
Run Code Online (Sandbox Code Playgroud)

应该显示,

另一个价值
另一个值

但他们显示,

初始值
初始值

为什么会这样?

Sot*_*lis 8

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

InnerClass#showValue()

public void showValue() {
    System.out.println(getValue());
    System.out.println(value);
}
Run Code Online (Sandbox Code Playgroud)

因为那些是私有的,getValue()并且value指的是外层的成员,因为你在同一个班级,即可以访问.内部类可以访问外部类私有成员.上述调用相当于

public void showValue() {
    System.out.println(TestInnerClass.this.getValue());
    System.out.println(TestInnerClass.this.value);
}
Run Code Online (Sandbox Code Playgroud)

因为你已经设定value

new TestInnerClass("Initial value")
Run Code Online (Sandbox Code Playgroud)

你看到"Initial value"被打印两次.无法访问private子类中的这些成员.


关键是:不要使子类内部类.


Roh*_*ain 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();
}
Run Code Online (Sandbox Code Playgroud)

执行此代码时,您将获得如下输出:

Outer@135fbaa4
Outer$Inner@45ee12a7
Outer@135fbaa4
Outer$Inner@330bedb4
Run Code Online (Sandbox Code Playgroud)

注意1如何第一和3个RD参考是相同的,而2 和4 是不同的.


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

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

public void showValue() {
    System.out.println(TestInnerClass.this.value);
    System.out.println(value);
}
Run Code Online (Sandbox Code Playgroud)

前两个印刷语句是等价的.它们将被编译为相同的字节代码:

 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
Run Code Online (Sandbox Code Playgroud)

您无法使用以下方法显式访问外部类成员this:

如果您明确尝试this在内部类中限定字段或方法访问表达式,则会出现编译器错误:

public void showValue() {
    System.out.println(this.value);  // this won't compile
}
Run Code Online (Sandbox Code Playgroud)

上面的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
}
Run Code Online (Sandbox Code Playgroud)

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

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

考虑一下你创建一个外部类实例:

new Outer("rohit").callShowValue();
Run Code Online (Sandbox Code Playgroud)

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

new Inner("rj").showValue();
Run Code Online (Sandbox Code Playgroud)

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

rj
rj
rohit
Run Code Online (Sandbox Code Playgroud)

你会注意到,this.value不同于Outer.this.value.

如果你做什么value领域private:

现在,当你创建外部类字段时private,当然你无法使用它来访问它this.value;.因此,第二个print语句将无法编译.

在这种情况下,直接访问该字段将是合格的this$0.现在将字段更改为valueprivate,并将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
}
Run Code Online (Sandbox Code Playgroud)

这就是问题所在.第一个print语句资格valuethisthis$0基于字段是否public还是private.


来你的具体问题:

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

public void showValue() {
    System.out.println(getValue());
    System.out.println(value);
}
Run Code Online (Sandbox Code Playgroud)

与:

public void showValue() {
    System.out.println(TestInnerClass.this.getValue());
    System.out.println(TestInnerClass.this.value);
}
Run Code Online (Sandbox Code Playgroud)

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

初始值
初始值