2013-10-13 2 views
9

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

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

должен показать,

Another value
Another value

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

Initial value
Initial value

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

ответ

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 элементам в подклассе.


Дело в том: не делать суб-классы внутренних классов.

2

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

8

Ключевым моментом здесь является понимание того, как внутренние классы обращаются к членам внешних классов. И как доступ к этим членам квалифицируется в случае private и non-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(); 
} 

При выполнении этого кода, вы получите такой вывод:

[email protected] 
[email protected] 
[email protected] 
[email protected] 

Обратите внимание, как 1 ул и 3 й ссылки одинаковы, в то время как 2 и и 4 th различны.


члены внешнего класса может быть доступны во внутреннем классе с использованием this$0 ссылки:

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

Вышеприведенный оператор печати не будет компилироваться, потому что 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 
} 

первых два оператора печати напечатает value поля экземпляра внутреннего класса, в то время как третий оператор печати напечатает value поля ограждающего экземпляра. Смущенный?

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

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

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

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

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

Теперь выход метода showValue() будет :

rj 
rj 
rohit 

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

Что делать, если вы сделаете value поле private:

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

И прямой доступ к полю в этом случае был бы квалифицирован с помощью this$0 на этот раз. Теперь изменить поле value частного и изменить метод 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$0 на основании того, является ли поле public или 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); 
} 

, который обращается к полю и методу включения экземпляра. И поле по-прежнему Начальная величина. И именно поэтому выход:

Initial Value
Initial Value

+0

Черт, чувак. В соответствующей заметке я также начну использовать свое имя в своих ответах. :) –

+0

@SotiriosDelimanolis haha ​​:) Конечно. –

1

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

Если вы замените showValue метод с этим:

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

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

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

Смежные вопросы