Подтвердить что ты не робот

Вызов статического метода в списке параметров super() действителен в Java. Зачем?

Посмотрите на следующий фрагмент кода в Java.

package trickyjava;

class A
{
    public A(String s)
    {
        System.out.println(s);
    }
}

final class B extends A
{
    public B()
    {
        super(method());      // Calling the following method first.      
    }

    private static String method()
    {
        return "method invoked";
    }
}

final public class Main
{
    public static void main(String[] args)
    {
        B b = new B();
    }
}

По соглашению конструктор super() в Java должен быть первым утверждением в соответствующем теле конструктора. В приведенном выше коде мы вызываем метод static в самом списке параметров конструктора super() super (method());.


Это означает, что в вызове super в конструкторе B() используется метод называемый ПЕРЕД вызовом супер! Это должно быть запрещено компилятором, но оно работает хорошо. Это несколько эквивалентно следующим утверждениям.

String s = method();
super(s);

Однако это незаконно вызывает ошибку времени компиляции, указывающую, что "вызов super должен быть первым выражением в конструкторе". Зачем? и почему это эквивалентно super (method()); допустимо, и компилятор больше не жалуется?

4b9b3361

Ответ 1

Ключевым моментом здесь является модификатор static. Статические методы привязаны к классу, методы экземпляра (обычные методы) привязаны к объекту (экземпляру класса). Конструктор инициализирует объект из класса, поэтому класс уже должен быть полностью загружен. Поэтому нет смысла вызывать статический метод как часть конструктора.

Последовательность событий для загрузки класса и создания объекта выглядит следующим образом:

  • класс нагрузки
  • инициализировать статические переменные
  • создать объект
  • инициализировать объект < - с конструктором
  • объект готов к использованию

(упрощенный *)

К моменту вызова конструктора объекта доступны статические методы и переменные.

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

Конструктор также называется инициализатором. Если вы выбросите исключение из конструктора и распечатаете трассировку стека, вы заметите, что он называется <init> в фрейме стека. Методы экземпляров могут быть вызваны только после того, как объект был создан. Невозможно использовать метод экземпляра в качестве параметра для вызова super(...) в вашем конструкторе.

Если вы создаете несколько объектов одного и того же класса, шаги 1 и 2 выполняются только один раз.

(* статические инициализаторы и инициализаторы экземпляров вычеркнуты для ясности)

Ответ 2

Да, проверяя спецификацию JVM (хотя, по общему признанию, старую):

В методе init экземпляра ссылка на "this" (включая неявную ссылку на возврат) может произойти до того, как произошел вызов другого метода инициализации в том же классе или метод init в суперклассе.

Это действительно единственное реальное ограничение, насколько я могу видеть.

Ответ 3

Цель запроса на вызов суперструктора сначала заключается в том, чтобы убедиться, что "супер объект" полностью инициализирован до того, как он будет использоваться (это не оправдывает этого, потому что суперструктор может протекать this, но это другое дело).

Вызов нестатического метода на this позволит методу видеть неинициализированные поля и поэтому запрещен. Статический метод может видеть только эти поля, если он передан this в качестве аргумента. Поскольку доступ к this и super является незаконным в выражениях вызова супер-конструктора, а вызов super происходит до объявления любых переменных, которые могут указывать на this, что позволяет звонить статическим методам в выражения вызова супер-конструктора безопасно.

Это также полезно, поскольку позволяет вычислять аргументы супер-конструктору произвольно сложным образом. Если бы вызовы статическим методам не были разрешены, в таком вычислении было бы невозможно использовать инструкции потока управления. Что-то простое:

class Sub extends Super {
    Sub(Integer... ints) {
        super(Arrays.asList(ints));
    }
}

было бы невозможно.

Ответ 4

Это одна из ситуаций, когда синтаксис java скрывает то, что действительно происходит, и С# делает его более понятным.

В С# ваш B будет выглядеть как

class B : A {
    public B() : base(method()) {
    }

    private static String method() {
        return "method invoker";
    }
}

Хотя синтаксис java помещает super(method) внутри конструктора, он на самом деле не называется там: все инициализации родителя запускаются до вашего конструктора подкласса. Код С# показывает это немного более четко; размещая super(method()) в первой строке конструктора java, вы просто говорите java, чтобы использовать параметризованный конструктор суперкласса, а не версию без параметров; таким образом вы можете передать переменные родительскому конструктору, и они будут использоваться при инициализации полей родительского уровня до, выполняемых вашим кодом конструктора.

Причина, по которой super(method()) действительна (как первая строка в java-конструкторе), связана с тем, что method() загружается статическими элементами - перед нестационарными, включая конструкторы - что позволяет для вызова не только до B(), но и до A(String). Говоря

public B() {
   String s = method();
   super(s);
}

вы говорите компилятору java для инициализации супер объекта с помощью конструктора по умолчанию (потому что вызов super() не является первой строкой), и вы готовы инициализировать подкласс, но компилятор затем запутывается когда он видит, что вы пытаетесь инициализировать с помощью super(String) после того, как super() уже запущен.

Ответ 5

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

В вышеприведенном случае, если java разрешает String s= method(); перед вызовом super, он открывает шлюз потока вещей, которые могут быть выполнены перед вызовом super. что может рисковать так много вещей, по сути, что позволяет использовать полупеченный класс. Который по праву не допускается. Это позволило бы изменить состояние объекта (некоторые из которых могут принадлежать родительскому объекту) до его правильного создания.

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

Ответ 6

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

PS: исправьте меня, если я ошибаюсь