Вызов статического метода в списке параметров super () допустим в Java.Зачем? - PullRequest
13 голосов
/ 12 ноября 2011

Давайте рассмотрим следующий фрагмент кода в 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 () вызывается метод ДО того, как будет выполнен вызов super!Это должно быть запрещено компилятором, но это работает хорошо.Это несколько эквивалентно следующим утверждениям.

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

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

Ответы [ 6 ]

10 голосов
/ 12 ноября 2011

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

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

  1. класс нагрузки
  2. инициализация статических переменных
  3. создать объект
  4. инициализировать объект <- с помощью конструктора </li>
  5. объект готов к использованию
* * Тысяча двадцать-одина (упрощенный *)

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

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

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

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

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

5 голосов
/ 12 ноября 2011

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

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

Насколько я понимаю, это действительно единственное реальное ограничение.

4 голосов
/ 12 ноября 2011

Целью требования, чтобы сначала был вызван супер-конструктор, является обеспечение того, чтобы «суперобъект» был полностью инициализирован перед тем, как его использовать.другое дело).

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

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

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

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

3 голосов
/ 12 ноября 2011

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

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

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

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

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

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

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

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

2 голосов
/ 12 ноября 2011

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

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

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

0 голосов
/ 20 декабря 2012

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

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

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