Что такое ключевое слово extends в Dart?

Ответ

extends — это ключевое слово в Dart, используемое для создания подкласса (наследника) от другого класса (суперкласса или родителя). Наследник получает все публичные и защищенные (protected через конвенцию _) поля и методы родителя, может их переопределять и добавлять новые.

Основные принципы:

  • Одиночное наследование: Класс может наследовать только от одного суперкласса.
  • Переопределение методов: Используется аннотация @override.
  • Конструкторы: Конструктор родителя вызывается с помощью super() в теле конструктора наследника.

Практический пример из Flutter: Создание кастомного виджета с дополнительной логикой.

// Базовый класс для всех экранов в приложении
abstract class BaseScreen extends StatelessWidget {
  final String screenTitle;
  const BaseScreen({required this.screenTitle, Key? key}) : super(key: key);

  // Общий метод для всех экранов
  AppBar buildAppBar(BuildContext context) {
    return AppBar(
      title: Text(screenTitle),
      centerTitle: true,
    );
  }

  // Абстрактный метод, который должны реализовать наследники
  Widget buildBody(BuildContext context);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: buildAppBar(context),
      body: buildBody(context),
    );
  }
}

// Конкретный экран, наследующий общую структуру
class HomeScreen extends BaseScreen {
  const HomeScreen({Key? key}) : super(screenTitle: 'Главная', key: key);

  @override
  Widget buildBody(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {},
        child: Text('Действие'),
      ),
    );
  }

  // Можно переопределить и метод родителя
  @override
  AppBar buildAppBar(BuildContext context) {
    final defaultAppBar = super.buildAppBar(context);
    return AppBar(
      title: defaultAppBar.title,
      actions: [IconButton(onPressed: () {}, icon: Icon(Icons.settings))],
    );
  }
}

Итог: extends — фундаментальный механизм для повторного использования кода и построения иерархий в объектно-ориентированном Dart.

Ответ 18+ 🔞

Давай разберем эту тему, чтобы в голове всё встало на свои места, а не болталось как хуй с горы.

extends — это, по сути, твой пропуск в мир наследования в Dart. Грубо говоря, ты берешь один класс и делаешь на его основе другой, который получает всё его добро: поля, методы, всё такое. Но есть нюансы, ёпта.

Что происходит, когда ты пишешь extends? Твой новый класс (наследник, он же подкласс) становится полноправным обладателем всего, что было публичным или условно-защищенным (это те поля с подчеркиванием _, которые по факту видны в том же файле) у родителя. Можешь этим пользоваться, можешь это переписывать (override), а можешь и своё свежее прикрутить.

Главные правила, которые надо запомнить:

  • Один родитель, и всё. В Dart нельзя наследоваться от десяти классов сразу, только одиночное наследование. Не как у некоторых, во все дыры давалка.
  • Переопределение — это святое. Хочешь поменять работу метода родителя? Пожалуйста, но ставь аннотацию @override, чтобы все (и особенно компилятор) поняли, что ты делаешь это осознанно, а не просто так, распиздяй.
  • Конструкторы. Родительский конструктор сам по себе не вызовется. Тебе надо явно позвать его через super() в теле конструктора наследника. Иначе — пиши пропало.

Зачем это всё, спросишь? Ну, ёбать копать, чтобы не копипастить один и тот же код в каждом втором классе. Создал один базовый класс с общей логикой — и потом от него только пляшешь.

Смотри, как это в Flutter'е выглядит на живом примере. Допустим, у нас в приложении куча экранов, и у каждого должен быть AppBar с заголовком. Чтобы не писать этот Scaffold раз по тридцать, сделаем базовый класс.

// Базовый класс для всех экранов. Делаем его абстрактным, чтобы с него нельзя было создать объект.
abstract class BaseScreen extends StatelessWidget {
  final String screenTitle;
  const BaseScreen({required this.screenTitle, Key? key}) : super(key: key);

  // Общий метод для построения шапки. Наследники могут его использовать или переписать.
  AppBar buildAppBar(BuildContext context) {
    return AppBar(
      title: Text(screenTitle),
      centerTitle: true,
    );
  }

  // А вот это — абстрактный метод. Он как долговая расписка.
  // Каждый наследник ОБЯЗАН будет сказать: "Вот, брат, держи, я тело экрана реализовал".
  Widget buildBody(BuildContext context);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: buildAppBar(context), // Используем общий метод
      body: buildBody(context), // А тут ждем реализацию от наследника
    );
  }
}

// А вот наш конкретный экран. Он наследуется от BaseScreen.
class HomeScreen extends BaseScreen {
  const HomeScreen({Key? key}) : super(screenTitle: 'Главная', key: key);

  // Выполняем долговое обязательство — реализуем buildBody.
  @override
  Widget buildBody(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {},
        child: Text('Нажми меня'),
      ),
    );
  }

  // А тут мы ещё и шапку родительскую решили улучшить.
  // Сначала вызываем метод родителя (super.buildAppBar), чтобы получить стандартную шапку,
  // а потом к ней ещё кнопку настроек прикручиваем.
  @override
  AppBar buildAppBar(BuildContext context) {
    final defaultAppBar = super.buildAppBar(context);
    return AppBar(
      title: defaultAppBar.title,
      actions: [IconButton(onPressed: () {}, icon: Icon(Icons.settings))],
    );
  }
}

Итог, чувак: extends — это краеугольный камень ООП в Dart. Не освоишь его — будешь потом везде копипастить код, а потом один фикс вносить в овердохуища мест, и сам от себя охуеешь. Используй грамотно, строй иерархии, и код станет чище и суше.