Java: нестатические вложенные классы и instance.super () - PullRequest
17 голосов
/ 14 мая 2010

Мне тяжело оборачиваться нестатическими вложенными классами в Java. Рассмотрим следующий пример: «Внутренний», а затем «Детский».

class Outer {
    class Inner {
        Inner() { System.out.println("Inner"); }
    }
}

public class Child extends Outer.Inner {
    Child(Outer o) {
        o.super();
        System.out.println("Child");
    }
    public static void main(String args[]) {
        new Child(new Outer());
    }
}

Я понимаю, что экземпляры Inner всегда должны быть связаны с Outer, и это относится и к Child, поскольку расширяет Inner. У меня вопрос, что означает синтаксис o.super() - почему он вызывает внутренний конструктор?

Я видел только простую super(args), используемую для вызова конструктора суперкласса, и super.method() для вызова версии переопределенного метода суперкласса, но никогда не в форме instance.super().

Ответы [ 5 ]

11 голосов
/ 14 мая 2010

Это называется "квалифицированным вызовом конструктора суперкласса".

Цитировать с здесь :

Явные операторы вызова конструктора можно разделить на два вида:

  • Альтернативные вызовы конструктора начинаются с ключевого слова this (возможно, с предваряющими явными аргументами типа). Они используются для вызова альтернативного конструктора того же класса.

  • Вызовы конструктора суперкласса начинаются либо с ключевого слова super (возможно, с предваряющим явными аргументами типа), либо с выражения Primary. Они используются для вызова конструктора прямого суперкласса. Вызовы конструктора суперкласса могут быть дополнительно подразделены:

  • Неквалифицированные вызовы конструктора суперкласса начинаются с ключевого слова super (возможно, с предваряющим явными аргументами типа).

  • Квалифицированные вызовы конструктора суперкласса начинаются с первичного выражения. Они позволяют конструктору подкласса явным образом указывать экземпляр объекта, который немедленно создается, в отношении прямого суперкласса (§8.1.3). Это может быть необходимо, когда суперкласс является внутренним классом.

10 голосов
/ 14 мая 2010

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

class Outer {
    static class Inner {
        final Outer outer;
        Inner(Outer outer) {
            this.outer = outer;
            System.out.println("Inner");
        }
    }
}

public class Child extends Outer.Inner {
    Child(Outer o) {
        super(o); // o.super();
        System.out.println("Child");
    }

    public static void main(String args[]) {
        new Child(new Outer());
    }
}

Глядя на это, вы должны понимать, что делал o.super ().

6 голосов
/ 14 мая 2010

Почему o.super() in Child в итоге вызывает Outer.Inner конструктор? Все просто: потому что Child extends Outer.Inner, а вызовы конструктора всегда связаны иерархией.

Вот небольшое расширение вашего фрагмента для иллюстрации:

class Outer {
    Outer() {
        System.out.println("Outer");
    }
    void outerMethod() { }
    class Inner {
        Inner() {
            System.out.println("OuterInner");
            outerMethod();              
        }
        String wealth;
    }
}
class OuterChild extends Outer {
    OuterChild() {
        System.out.println("OuterChild");
    }
}
public class OuterInnerChild extends Outer.Inner {
    OuterInnerChild(Outer o) {
        o.super();
        System.out.println("OuterInnerChild");
        this.wealth = "ONE MILLION DOLLAR!!!";
    }
    public static void main(String args[]) {
        System.out.println(new OuterInnerChild(new Outer()).wealth);
        new OuterChild();
    }
}

Это печатает:

Outer
OuterInner
OuterInnerChild
ONE MILLION DOLLAR!!!
Outer
OuterChild

Некоторые ключевые наблюдения:

  • Поскольку OuterInnerChild extends Outer.Inner, он наследует wealth, как и обычная семантика подкласса.
    • И точно так же, как нормальная семантика подкласса, конструктор OuterInnerChild связывается с конструктором Outer.Inner
  • Потому что OuterChild extends Outer, его конструктор цепочки, даже если не вызывается явно
    • Неявно или явно, конструктор объединяет иерархию

Но почему компилятор требует, чтобы конструктор OuterInnerChild занял Outer o, и что o.super() вызывается?

Теперь, что специфично для внутренней семантики класса: это сделано для того, чтобы все экземпляры OuterInnerChild имели включающий Outer экземпляр для Outer.Inner, суперкласс OuterInnerChild. В противном случае конструктор Outer.Inner не будет иметь включающий экземпляр Outer для вызова outerMethod() on.

2 голосов
/ 14 мая 2010

Концептуально, нестатический внутренний класс «принадлежит» определенному объекту. Это похоже на то, что каждый получает свою версию класса, как нестатическое поле или метод, принадлежащий определенному объекту.

Так вот почему у нас есть забавный синтаксис, такой как instance.new Inner() и instance.super() - для контекстов, где ответ на вопрос «но чей Inner?» Не сразу очевиден. (В нестатическом методе внешнего класса вы можете просто сказать new Inner(), и, как обычно, это сокращение от this.new Inner().)

0 голосов
/ 14 мая 2010

Всегда не забывать об основных принципах, в процессе вызова конструктора подкласса всегда сначала создается родительский класс независимо от внутренних / внешних классов. В вашем сценарии, поскольку вы расширяете внутренний класс, а ваш внутренний класс является членом родительского класса, который необходимо создать, а затем вызвать фактический конструктор внутреннего класса.

...