Странная ситуация для «не может ссылаться на это до вызова конструктора супертипа» - PullRequest
15 голосов
/ 01 августа 2010

Почему этот код не компилируется?

public class A {
    public class B extends A {
        public B(A a) { }
    }
    void foo() {
        A a = new A();
        new B(a) { };
    }
}

A.java:[7,17] cannot reference this before supertype constructor has been called

Компиляция прошла успешно, если было выполнено одно из следующих изменений:

  • B является приватным, а не публичным
  • строка 7 гласит new B(A); вместо new B(A) { }

Использование версии javac: 1.6.0_20

Ответы [ 2 ]

20 голосов
/ 02 августа 2010

Следует отметить, что 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.

0 голосов
/ 02 августа 2010

Не уверен, что именно цель, но попробуйте это.Заметьте, что я также исключил передачу A в качестве аргумента, поскольку это нестатические классы, с которыми они уже связаны.Я включил синтаксис для ссылки на внешний класс «this» для тех ситуаций, когда внутренний класс может закрывать поле / метод внешнего класса

public class A {

    protected String field = "a";

    public class B extends A {
        protected String field = "b";

        public B() {
            System.out.println("" + A.this.field + " " + this.field);
        }

        @Override
        void foo() {
            System.out.println("b.foo()");
        }
    }

    void foo() {
        A a = new A();
        a.field = "a2";
        B b = a.new B() {
            @Override
            void foo() {
                System.out.println("b.anon.foo()");
            }
        };

        b.foo();
    }

    public static void main(String[] args) {
        new A().foo();
    }
}

Запуск этого приведет к выводу:

a2 b
b.anon.foo()

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...