Следует отметить, что Eclipse, javac
и Intellij IDEA демонстрируют различия в поведении в отношении этих фрагментов.javac
и поведение Java Puzzlers используется для справки в этом обсуждении.
Мне удалось сократить фрагмент до следующего:
public class A {
class B extends A {
}
void foo() {
new B() { }; // DOES NOT COMPILE!!
}
}
Этот сценарий обсуждается в Java Puzzlers , Головоломка 90: Это абсурд, это боль, это суперкласс!
Ниже приведен фрагмент:
public class Outer { // "A"
class Inner1 extends Outer {} // "B"
class Inner2 extends Inner1 {} // "B" anonymous
}
// DOES NOT COMPILE!!
Проблема в том, что из-за того, как определен конструктор по умолчанию, у нас действительно есть следующее:
// Same as above but with default constructor included explicitly
public class Outer {
class Inner1 extends Outer {
Inner1() { super(); }
}
class Inner2 extends Inner1 {
Inner2() { super(); }
}
}
// STILL DOES NOT COMPILE!!
Проблема в том, что Inner2
Суперкласс сам по себе является внутренним классом Inner1
, что делает конструктор по умолчанию Inner2
недопустимым, поскольку для него требуется, чтобы в конструктор был включен включающий экземпляр.
Способ устранения "грубой силы"проблема состоит в том, чтобы предоставить это явно с помощью квалифицированного выражения this
:
// "brute-force" fix
public class Outer {
class Inner1 extends Outer {
Inner1() { super(); }
}
class Inner2 extends Inner1 {
Inner2() { Outer.this.super(); }
}
}
// NOW COMPILES!
Однако, загадка предписывает, что такой сложной ситуации лучше всего избегать в первую очередь.Вот несколько цитат:
Это компилируется, но это сложно для ума.Есть лучшее решение: всякий раз, когда вы пишете класс-член, спросите себя, действительно ли этому классу нужен включающий экземпляр?Если ответ нет, сделайте это static
.Внутренние классы иногда полезны, но они могут легко внести сложности, которые затрудняют понимание программы.Они имеют сложные взаимодействия с генериками (головоломка 89), отражением (головоломка 80) и наследованием (эта головоломка).Если вы объявите Inner1
равным static
, проблема исчезнет.Если вы также объявите Inner2
равным static
, вы действительно сможете понять, что делает программа: действительно, хороший бонус.
Таким образом, редко бывает уместно, чтобы один класс был и внутренним, иподкласс другого.В целом, редко бывает целесообразно расширить внутренний класс;если нужно, подумайте долго и внимательно о включающем экземпляре.Кроме того, предпочитайте static
вложенные классы не static
.Большинство классов-членов могут и должны быть объявлены static
.