Почему this () и super () должны быть первым оператором в конструкторе?

557
задан Martin Broadhurst 28 January 2018 в 08:37
поделиться

4 ответа

Конструктор родительского класса должен быть вызван перед конструктором подкласса . Это гарантирует, что если вы вызовете какие-либо методы родительского класса в своем конструкторе, родительский класс уже будет правильно настроен.

То, что вы пытаетесь сделать, передать аргументы в суперконструктор совершенно законно, вам просто нужно чтобы построить эти аргументы встроенными, как вы это делаете, или передать их вашему конструктору, а затем передать их в super :

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

Если компилятор не принудил к этому, вы можете сделать это:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

В случаях где родительский класс имеет конструктор по умолчанию, вызов super вставляется автоматически компилятором . Поскольку каждый класс в Java наследуется от объекта , Конструктор объектов нужно как-то вызывать и запускать первым. Автоматическая вставка super () компилятором позволяет это. Принуждение к появлению super первым приводит к тому, что тела конструктора выполняются в правильном порядке: Объект -> Родитель -> Дочерний -> ChildOfChild -> SoOnSoForth

179
ответ дан 22 November 2019 в 22:09
поделиться

Вы можете использовать блоки анонимного инициализатора для инициализации полей в дочернем элементе перед вызовом его конструктора. Этот пример демонстрирует:

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

class Parent {
    public Parent() {
        System.out.println("In parent");
    }
}

class Child extends Parent {

    {
        System.out.println("In initializer");
    }

    public Child() {
        super();
        System.out.println("In child");
    }
}

Это выведет:

В родительском
В инициализаторе
In child

2
ответ дан 22 November 2019 в 22:09
поделиться

Я почти уверен (те, кто знаком со спецификацией Java, вмешивается), что это предотвращает (а) использование частично сконструированного объекта и (б) принудительное использование родительского объекта конструктор класса для построения на "свежий "объект.

Вот некоторые примеры" плохого ":

class Thing
{
    final int x;
    Thing(int x) { this.x = x; }
}

class Bad1 extends Thing
{
    final int z;
    Bad1(int x, int y)
    {
        this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
        super(x);
    }        
}

class Bad2 extends Thing
{
    final int y;
    Bad2(int x, int y)
    {
        this.x = 33;
        this.y = y; 
        super(x); // WHOOPS! x is supposed to be final
    }        
}
13
ответ дан 22 November 2019 в 22:09
поделиться

Потому что так говорит JLS. Можно ли изменить JLS совместимым образом, чтобы разрешить это? Ага.

Однако это усложнило бы спецификацию языка, которая и так уже более чем достаточно сложна. Это было бы не очень полезно, и есть способы обойти это (вызовите другой конструктор с результатом статического метода или лямбда-выражения this (fn ()) - метод вызывается перед другой конструктор, а значит, и суперконструктор). Таким образом, соотношение мощности и веса выполнения изменения неблагоприятно.

Обратите внимание, что это правило само по себе не предотвращает использование полей до того, как суперкласс завершит построение.

Рассмотрим эти недопустимые примеры.

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

super(this instanceof SubClass);
// this.getClass() would be /really/ useful sometimes.

Этот пример является допустимым. , но "неверно".

class MyBase {
    MyBase() {
        fn();
    }
    abstract void fn();
}
class MyDerived extends MyBase {
    void fn() {
       // ???
    }
}

В приведенном выше примере, если MyDerived. fn требовал аргументов от конструктора MyDerived , которые нужно было бы обработать с помощью ThreadLocal . ; (

Между прочим, начиная с Java 1.4, синтетическое поле, содержащее внешний this , назначается перед вызовом суперконструктора внутренних классов. Это вызвало странные события NullPointerException в коде, скомпилированном для нацелены на более ранние версии.

Обратите также внимание на то, что при наличии небезопасной публикации конструкция может просматриваться в другом порядке, если не приняты меры предосторожности.

Редактировать март 2018 г .: В сообщении Записи: строительство и проверка Oracle предлагает снять это ограничение (но в отличие от C #, этот будет определенно неназначенным (DU) перед объединением конструкторов).

Исторически сложилось так, что this () или super () должны быть первыми в конструкторе. Эта ограничение никогда не было популярным и воспринималось как произвольное. Были ряд тонких причин, в том числе проверка invokespecial, который способствовал этому ограничению. С годами мы обратились к ним на уровне ВМ, до такой степени, что становится целесообразно рассмотреть снятие этого ограничения, не только для записей, но для всех конструкторов.

45
ответ дан 22 November 2019 в 22:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: