Почему локальный класс, который расширяет внутренний класс, не может получить доступ к экземпляру, вмещающему внутренний класс?
(Я продолжаю перечитывать заголовок вопроса и думаю о том, как нелепо это должно выглядеть, но уверяю вас, что это лучшее описание проблемы, и у меня есть реальное приложение, где это лучшая структура. Клянусь, я не псих.)
Учтите следующее. Каждый блок представляет собой отдельный файл:
package myPackage;
public class A {
public int i;
public A(int i) {
this.i = i;
}
public class B {
}
}
package myPackage;
import myPackage.A.B;
public class Main {
public static void main(String[] args) {
class C extends B {
public C(A enclosingInstance) {
enclosingInstance.super();
}
public void show() {
System.out.println(A.this.i);
}
}
A myA = new A(2);
C myC = new C(myA);
myC.show();
}
}
Обратите внимание, что enclosingInstance
дело в том, чтобы решить проблему, связанную с промежуточными вызовами конструктора. Смотрите "Почему внешние классы не могут расширять внутренние классы?",
Я ожидаю, что на выходе будет "2". Но вместо этого у меня есть ошибка компиляции на System.out.println(A.this.i);
:
Ни один включающий экземпляр типа A не доступен в области видимости
Я думаю, что программная концепция, которую я пытаюсь решить, является разумной: создайте новый тип B внутри main
дать A, который использует вещи из A, к которым могут обращаться типы B.
Так что я делаю не так, или почему это невозможно в Java?
РЕДАКТИРОВАТЬ / ОБНОВИТЬ: Обратите внимание, что та же ошибка появляется, когда код в main
перемещен в нестатический метод. То есть я пытался переместить все внутри static void main
к новому, нестатическому методу class Main
называется go()
, Потом я поменял static void main
до одной строки new Main().go();
, Ошибка в том же месте. Так что это не проблема class C
будучи определенным в статическом контексте.
3 ответа
Ты хочешь A.this
ссылаться на включающий экземпляр B
пример. Но зачем это? Это не то, что означает синтаксис. A.this
будет означать вложение A
экземпляр C
экземпляр, и это не имеет смысла, потому что C
не внутренний класс A
,
Чтобы сделать это более понятным, вот пример, где C
это внутренний класс A
,
public class A {
public int i;
public A(int i) {
this.i = i;
}
public class B {
void foo() {
System.out.println(A.this.i);
}
}
public class C extends B {
C(A a) {
a.super();
}
void bar() {
System.out.println(A.this.i);
}
}
public static void main(String[] args) {
A a1 = new A(1);
A a2 = new A(2);
C c = a1.new C(a2);
c.foo();
c.bar();
}
}
Вот C
продолжается B
и оба C
а также B
являются внутренними классами A
, Поэтому любой C
имеет ограждение A
экземпляр, и он также имеет вмещающий A
случай, когда рассматривается как B
и эти включающие экземпляры различны (что подтверждается тем фактом, что foo
а также bar
печатать разные номера).
Так, A.this
не может означать то, что вы хотите, чтобы это значило, потому что это уже означает что-то еще. Я предполагаю, что причина, по которой разработчики языка не придумали другой синтаксис для обозначения включающего экземпляра суперкласса, заключается в том, что такой синтаксис был бы очень сложным, с небольшой отдачей (простые обходные пути уже существуют).
Это абсурдный код, который вы никогда не должны писать для производства.
Это частично объясняется в документации по явным вызовам конструктора
Квалифицированные вызовы конструктора суперкласса начинаются с
Primary
выражение илиExpressionName
, Они позволяют конструктору подкласса явным образом указывать экземпляр объекта, который немедленно создается, относительно прямого суперкласса (§8.1.3). Это может быть необходимо, когда суперкласс является внутренним классом.
Все это, чтобы сказать, что C
это местный класс ( который является inner
класс, который является своего рода глупостью, потому что если вы объявите это в static
метод не существует включающего экземпляра), который является подклассом B
но не вложенный класс A
, Таким образом, нет включающего экземпляра. Экземпляр C
не имеет включающего экземпляра. (Хотя это было бы, если бы вы объявили это в методе экземпляра, но это будет экземпляр Main
.)
Непосредственно включающий экземпляр вновь созданного объекта (из JLS) указывается косвенно через параметр конструктора.
Вы должны хранить его самостоятельно
private A enclosingInstance;
public C(A enclosingInstance) throws CloneNotSupportedException {
enclosingInstance.super();
this.enclosingInstance = enclosingInstance;
}
и с тех пор A#i
является public
, вы можете получить к нему доступ нормально
public void show() {
System.out.println(enclosingInstance.i);
}
С предоставленной информацией я бы сделал это:
public class B {
protected A getOuterInstance() {
return A.this;
}
}
и просто дай C
наследовать и использовать этот метод. Я знаю, что вам не нравится этот метод, но это самый простой ответ, который я вижу. Имея больше информации, я бы, вероятно, предложил бы проект, в котором бы не использовался какой-либо внутренний класс, поскольку это не обычный вариант использования для внутренних классов.