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