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