Рубрики
Без рубрики

Вызов супер() – [ООП и Java #12]

P.S. Наконец-то вернулся с еще одной краткой статьей о том, что я узнал во время (еще одного) позднего ni… С пометкой “информатика”, java.

ООП и Java (Серия из 14 частей)

P.S. Наконец-то вернулся с еще одной краткой статьей о том, что я узнал во время (еще одной) ночной дискуссии.

Мотивация

Просматривая кодовую базу проекта brownfield, над которым я сейчас работаю в рамках своей школьной работы, я с первого взгляда нашел следующие строки странными.

public class StorageManager implements Storage {
    // other details omitted ...
    /**
     * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
     */
    public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
        super();
        this.addressBookStorage = addressBookStorage;
        this.userPrefsStorage = userPrefsStorage;
    }

Приведенный выше код относится к конкретной реализации интерфейса с именем Хранилище и конкретная строка, которая меня интересовала, это: супер(); .

Причины, по которым я чувствовал, что звонок в super был необычным:

  1. Диспетчер хранилища не распространяется ни на один родительский класс, поэтому я предположил, что вызов super означает вызов конструктора Объекта , зачем вообще это делать?
  2. Если каким-то образом вызов super связан с интерфейсом, который Менеджер хранилища реализует, это не имеет смысла, потому что интерфейсы в любом случае не могут быть созданы.
  3. Хотя я знал, что вызов super вызывает родительский конструктор, и его можно использовать для передачи соответствующих параметров родителю, я не вижу практической цели в приведенном выше коде.

Обсуждение

Я разместил свой вопрос на форуме класса и смог получить несколько полезных ответов. Поэтому первое, что я сделал, это попытался удалить super(); чтобы проверить, имеет ли это практическое значение. Тесты все еще проходили, и никаких заметных проблем при перезапуске приложения не наблюдалось. Это доказывало одно: звонок в super в данном случае был ненужным и бессмысленным.

Я собирался завершить вышеизложенным фактом, а также практическим советом всегда проверять код, чтобы проверить мои подозрения. Однако последующий комментарий моего профессора заинтриговал меня. В частности, эта строка ниже не имела для меня смысла:

Явный вызов super(…) необходим только в том случае, если родительский класс не имеет конструктора без параметров или…

Я не был уверен, к чему относится вышеприведенное утверждение, и с помощью нескольких сокурсников, которые присоединились к обсуждению, и еще немного погуглив, я наконец понял это утверждение. Это было воплощением незнания того, чего я не знаю.

Анализ

Чтобы понять приведенное выше утверждение, нам необходимо знать следующие факты, которые я извлек из ссылок, приведенных в конце этой статьи.

  • Если в классе не определен конструктор, компилятор Java автоматически создает конструктор без аргументов (без аргументов), который просто вызывает вызов super(). То есть:
public class Foo {
}

эквивалентно

public class Foo {
  Foo() {
    super();
  }
}
  • Конструктор без аргументов по умолчанию не будет автоматически сгенерирован, если был определен один (или несколько) конструкторов. Другими словами, вам нужно явно определить конструктор без аргументов, если были определены другие конструкторы. То есть:
public class Foo {
  int bar;
  Foo(int bar) {
    this.bar = bar;
  }
}

становится ли не следующим по умолчанию:

public class Foo {
  int bar;
  Foo(int bar) {
    this.bar = bar;
  }
  Foo() {
    super();
  }
}
  • Если у непосредственного суперкласса нет конструктора по умолчанию (он определяет некоторые конструкторы, но не определяет конструктор без аргументов), вы получите ошибку компиляции при выполнении вызова super(). То есть:
public class Foo {
  int bar;
  Foo(int bar) {
    this.bar = bar;
  }
}

если выше указан класс Foo , то следующее приведет к ошибке компиляции:

public class Bar extends Foo {
  Bar() {
    super();
  }
}

Или это:

public class Bar extends Foo {
  Bar() {
  // some lines as long as they are not super
  }
}

Или это:

public class Bar extends Foo {
  // no constructor provided
}

Единственный верный способ – это что-то вроде этого:

public class Bar extends Foo {
    Bar() {
    super(10);
  }
}
  • Объяснение вышесказанному: Java гарантирует, что метод конструктора класса вызывается всякий раз, когда создается экземпляр этого класса. Это также гарантирует, что конструктор вызывается всякий раз, когда создается экземпляр любого подкласса. Чтобы гарантировать этот второй пункт, Java должна гарантировать, что каждый метод конструктора вызывает свой метод конструктора суперкласса. Таким образом, если первый оператор в конструкторе явно не вызывает другой конструктор с помощью this() или super(), Java неявно вставляет вызов super(); то есть он вызывает конструктор суперкласса без аргументов. Если у суперкласса нет конструктора, который не принимает аргументы, этот неявный вызов вызывает ошибку компиляции.

Последний пример

Подводя итог, я расскажу о другом примере из сообщения StackOverflow:

public class Base {
   public Base(int foo) {
   }
}

public class Subclass extends Base {
   public Subclass() {
      super(15);
   }
}

Если конструктор Подкласса не включает вызов super(15) , Java неявно вставит super() в качестве первой строки конструктора подкласса , что вызовет ошибку компиляции в случае, когда класс Base не имеет конструктора без параметров( Base() ), который, к сожалению, не будет автоматически сгенерирован, поскольку существует существующий конструктор ( Base(int foo) ).

Рекомендации

ООП и Java (Серия из 14 частей)

Оригинал: “https://dev.to/tlylt/calling-super-oop-java-12-57d8”