Как сделать внешний класс унаследованным от внутреннего класса? - PullRequest
8 голосов
/ 16 апреля 2011

Как я могу сделать что-то вроде этой работы:

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
  }
}

class OuterExtendsInner extends Outer.InnerBase {
  OuterExtendsInner(Outer o) { o.super(); }
  void method() {
    // How do I use some_member here?
    // Writing Outer.this.some_member -> error about Outer not being an enclosing class
    // Writing just some_member -> no-go, either
  }
}

Обходной путь должен иметь метод в InnerBase, который возвращает Outer.this и вызывает THAT из производных классов, но есть ли другой способ?

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

Ответы [ 7 ]

3 голосов
/ 16 апреля 2011

Проблема здесь в том, что синтетическое поле, которое связывает InnerBase с Outer, является частным полем. Таким образом, мы можем получить доступ только к внешнему объекту изнутри InnerBase, или если какой-то метод или поле предоставляет ссылку на тот же объект.

3 голосов
/ 16 апреля 2011

Вы можете сделать это в OuterExtendsInner:

class OuterExtendsInner extends Outer.InnerBase {
    Outer o;

    OuterExtendsInner(Outer o) {
        o.super();
        this.o = o;
    }

    void method() {
        // now you can reference o.some_member
        int x = o.some_member;
    }
}
1 голос
/ 20 июля 2011

Просто есть метод получения в InnerBase?

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
    protected int getSome_Member() // This is possible, because Abstract classes can have non-abstract methods.
    {
       return some_member;
    }
  }
}

class OuterExtendsInner extends Outer.InnerBase {
  OuterExtendsInner(Outer o) { o.super(); }
  void method() {
       // you can access "some_member" now
      int myNumber = getSome_Member();

  }
}
1 голос
/ 16 апреля 2011

Я не пробовал ответить WhiteFang34. Это может сработать, но я не совсем понимаю ...

Если вы действительно хотите определить расширение вашего внутреннего класса в другом месте, чем во внешнем классе, наиболее естественным было бы определить его как расширение внутреннего класса в другом внешнем расширении вашего внешнего класса следующим образом:

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
  }
}

class OuterExtendsOuter extends Outer {
  class InnerExtendsInner extends Outer.InnerBase {
    void method() {
       System.out.println(some_member);
    }
  }
}

Я на самом деле тоже не запускал этот код, но он должен работать.

Обновление:

Основываясь на ветке комментариев, я теперь скомпилировал и запустил как мой код выше, так и код WhiteFang34.

Оба на самом деле работают, но, как отмечено в комментариях Поло Эбермана, оба создают две копии внешнего внутри экземпляра внутреннего класса.

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

Просто сделайте ваши расширенные внутренние классы живыми внутри того же внешнего класса!

Обновление 2:

То, что происходит в моем коде на основе проверки во время выполнения с использованием отладчика и проверки результатов проверок классов в javap, заключается в том, что как InnerBase, так и OuterExtendsOuter$InnerExtendsInner имеют синтетические частные конечные поля с именем this$0. Поскольку конструкторы не определены явно, используются конструкторы по умолчанию и фрагмент кода

    OuterExtendsOuter outer = new OuterExtendsOuter();
    Outer.InnerBase inner = outer.new InnerExtendsInner();

заставляет эти два поля ссылаться на outer.

Другими словами, комментарий Пауло совершенно правильный.

Путем дальнейших экспериментов то же самое на самом деле происходит, если вы расширяете InnerBase в другом внутреннем классе Outer, поэтому он имеет мало общего с его определением в том же внешнем классе или его расширением, но находится в факт, как обычно обрабатываются нестатические внутренние классы.

Я подозреваю, что это где-то задокументировано, но я этого не видел.

Вероятно, лучше всего смешивать наследование и внутренние классы как можно меньше!

1 голос
/ 16 апреля 2011

Ответ: вы не можете, потому что это нарушит инкапсуляцию.Только InnerBase может иметь доступ к атрибутам Outer, но не OuterExtendsInner.Это не прямое наследство.InnerBase не наследует Outer.

0 голосов
/ 27 декабря 2011

Внешний класс может расширять внутренний класс, если внутренний класс скомпилирован в «.class».

Теперь, каждый раз, когда вы компилируете внешний класс, он встречает «extends innerclass», который равен

еще не скомпилировано, и компилятор сгенерирует исключение NoClassDefException или ClassNotFoundException.

Не так ли?Так что вы никогда не скомпилируете этот внутренний класс.Если вы можете преодолеть эту проблему

, вы также можете расширить внутренний класс :).

0 голосов
/ 16 апреля 2011

Ну, ваша проблема в том, что каждый экземпляр InnerBase (я знаю, что он абстрактный) должен иметь ссылку на объект Outer. Это часть семантики вложенных классов. Для OuterExtendsInner потребуется такая ссылка. Вы можете избежать этого, создав InnerBase static вложенный класс.

...