Доступ к членам внешнего класса из внутреннего класса, расширяющего сам внешний класс

в приведенном ниже фрагменте кода внутренний класс наследует внешний класс.

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 класс, а затем вызывает callShowValue() метод.

на callShowValue() метод вызывает другую строку -Another value для установки в личное поле value на TestInnerClass класс перед вызовом showValue() способ из InnerClass расширения TestInnerClass.

соответственно, следующие два утверждения внутри showValue() метод

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

должны отображаться,

другое значение
Другое значение

но они показывают,

начальной стоимостью
Начальное значение

почему это происходит?

4 ответов


метод 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 as

new TestInnerClass("Initial value")

видишь "Initial value" печатается дважды. нет никакого способа получить доступ к этим private члены в подклассе.


дело в том, что не делайте подклассы внутренними классами.


ключ здесь-понять, как внутренние классы обращаются к членам внешних классов. И как доступ к этим членам квалифицирован в случае private и non-private членов. (Примечание: я буду говорить о не-static внутренние классы здесь, так как вопрос только об этом).

внутренние классы хранят ссылку на заключительный экземпляр:

внутренний класс хранит ссылку на экземпляр включения в поле. Поле называется this. Заключающий экземпляр всегда привязан к объекту внутреннего класса. Когда вы создаете объект внутреннего класса внутри класса включения, опорное значение this остается неизменным для всех них, но this ссылка будет отличаться.

доступ this поле с помощью 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

уведомления как 1st и 3rd ссылки одинаковы, в то время как 2nd и 4th разные.


внешние члены класса могут быть доступны во внутреннем классе с помощью this ссылки:

когда вы получаете доступ к полям или любому другому члену внешнего класса из внутреннего класса, выражение доступа квалифицируется автоматически this. Вы явно квалифицируете доступ участника как this используя OuterClass.this ссылка. Итак, рассмотрим поле value в вашем внешнем классе было public, затем в 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: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: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
}

приведенный выше оператор печати не будет компилироваться, потому что value не является полем самого внутреннего класса. Это поле внешнего класса. this относится к внутреннему экземпляру класса, а не к внешнему.


история меняется, когда внутренний класс расширяет внешний класс:

когда ваш внутренний класс расширяет внешний класс, тогда все начинает идти странно. Потому что в таком случае, квалификация поля или метода доступа с помощью this будет действительным для членов, не являющихся частными. Для private члены, это все равно не будет действительным, как private члены не наследуются.

в случае унаследованного внутреннего класса прямой доступ к внешним членам класса квалифицируется с this. Это означает, что они будут доступны как члены внутреннего класса. При явной квалификации доступа с помощью Outer.this будет ссылаться на поле заключительного экземпляра - this.

учитывая 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 поле внутреннего экземпляра класса, в то время как 3-й оператор печати будет печатать value поле заключительного экземпляра. Смущен?

помните, я сказал, Когда вы создаете несколько экземпляров внутреннего класса изнутри внешнего класса, они будут иметь то же самое this ссылка.

рассмотрим создание внешний экземпляр класса:

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

а потом в callShowValue() метод, вы создаете экземпляр внутреннего класса:

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

Итак, вывод showValue() способ такой:

rj
rj
rohit

вы бы заметили это,this.value отличается от Outer.this.value.

что если сделать value поле private:

теперь, когда вы делаете поле внешнего класса private, то, конечно, вы не можете получить доступ к нему с помощью this.value;. Так, 2-й оператор печати не будет компилироваться.

и прямой доступ к полю в этом случае был бы квалифицирован с this в этот раз. теперь измените поле value private, и изменить showValue() метод as:

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 на основе того, является ли поле public или private.


приходить к вашему бетону проблема:

теперь в вашем коде, так как и getValue() метод private, the 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 различных отношения между TestInnerClass и InnerClass.

но есть небольшой поворот в истории.. есть два объекта! и проблема в том, что мы ставим "Другое Значение" на другой объект! и печать стоимостью от старого..

Первый Объект:

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

выше метод в тестовом классе создает экземпляр TestInnerClass с "начального значения"

второй объект:

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

С InnerClass распространение TestInnerClass другое новый экземпляр TestInnerClass создается с "Другое Значение". Но мы печатаем значение из старого объекта, а не из второго.


другие ответы хорошо объяснили, почему вы получаете результаты, которые видите (у вас есть два экземпляра TestInnerClass и обращаются к первому), однако на самом деле есть способ получить доступ к частным членам TestInnerClass в своем подклассе - ключевое слово super.

если вы замените showValue способ с этим:

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

вы получите результат, который вы ожидали.

Я бы также предложил, что если вы решите сделать это, вы сделаете InnerClass статический внутренний класс, потому что она больше не нужна ссылка на экземпляр внешнего класса.