Почему локальный класс, который расширяет внутренний класс, не может получить доступ к экземпляру, вмещающему внутренний класс?

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

Учтите следующее. Каждый блок представляет собой отдельный файл:

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

Другие вопросы по тегам