Конструкторы и наследование
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 41 из 110.
Когда дочерний класс расширяет родительский, конструктор родителя должен быть вызван для правильной инициализации унаследованных полей. Dart предоставляет гибкие способы обработки этого через список инициализации с использованием super.
Если у родителя есть параметризованный конструктор, дочерний класс должен вызывать его явно:
class Person {
String name;
int age;
Person(this.name, this.age);
}
class Student extends Person {
String school;
Student(String name, int age, this.school) : super(name, age);
}В Dart 2.17 появились супер-параметры — более лаконичный синтаксис, который передает параметры напрямую в конструктор родительского класса:
class Student extends Person {
String school;
// super.name и super.age передаются напрямую в конструктор Person
Student(super.name, super.age, this.school);
}При работе с именованными конструкторами вы указываете, какой родительский конструктор следует вызвать:
class Employee extends Person {
String department;
Employee(super.name, super.age, this.department);
Employee.intern(String name, this.department) : super(name, 18);
}Основное правило простое: родительский класс должен быть полностью инициализирован до того, как дочерний класс добавит свои собственные свойства. Независимо от того, используете ли вы традиционный вызов super() или более новые супер-параметры, Dart гарантирует, что цепочка наследования правильно выстроена от родителя к потомку.
Задание
ЛегкоДавайте создадим систему инвентаризации товаров, которая демонстрирует, как конструкторы работают с наследованием. Вы создадите иерархию, в которой различные типы товаров расширяют базовый класс продукта, и каждый из них требует правильной инициализации через родительские конструкторы.
Вы организуете свой код в двух файлах:
product.dart: Определите здесь иерархию ваших продуктов:- Класс
Productсо свойствамиString nameиdouble price, конструктором, принимающим оба значения, и методомinfo(), который выводит[name]: $[price] - Класс
Book, который расширяетProductи добавляет свойствоString author. Используйте супер-параметры для передачиnameиpriceв родительский конструктор - Класс
Electronics, который расширяетProductи добавляет свойствоint warranty(в месяцах). Используйте традиционный вызовsuper()в списке инициализации для передачи значений родителю
- Класс
main.dart: Импортируйте файл с продуктами и продемонстрируйте оба подхода к наследованию конструкторов:- Создайте
Bookс именем'Dart Essentials', ценой29.99и автором'Jane Smith' - Вызовите его метод
info(), затем выведитеAuthor: [author] - Выведите пустую строку
- Создайте
Electronicsс именем'Wireless Mouse', ценой45.50и гарантией24 - Вызовите его метод
info(), затем выведитеWarranty: [warranty] months
- Создайте
Оба дочерних класса наследуют метод info() от Product без необходимости его переопределения. Ключевое различие заключается в том, как они вызывают родительский конструктор: Book использует более чистый синтаксис супер-параметров, в то время как Electronics использует традиционный подход с super() в списке инициализации.
Ожидаемый результат:
Dart Essentials: $29.99
Author: Jane Smith
Wireless Mouse: $45.5
Warranty: 24 monthsШпаргалка
Когда дочерний класс расширяет родительский, необходимо вызвать конструктор родителя для правильной инициализации унаследованных полей.
Традиционный подход — явный вызов конструктора родителя в списке инициализации:
class Person {
String name;
int age;
Person(this.name, this.age);
}
class Student extends Person {
String school;
Student(String name, int age, this.school) : super(name, age);
}Super-параметры (Dart 2.17+) — прямая передача параметров в конструктор родителя:
class Student extends Person {
String school;
Student(super.name, super.age, this.school);
}Именованные конструкторы — указание того, какой конструктор родителя следует вызвать:
class Employee extends Person {
String department;
Employee(super.name, super.age, this.department);
Employee.intern(String name, this.department) : super(name, 18);
}Попробуйте сами
import 'product.dart';
void main() {
// TODO: Создайте Book с:
// - name: 'Dart Essentials'
// - price: 29.99
// - author: 'Jane Smith'
// TODO: Вызовите метод info() книги
// TODO: Выведите "Author: [author]"
// TODO: Выведите пустую строку
// TODO: Создайте Electronics с:
// - name: 'Wireless Mouse'
// - price: 45.50
// - warranty: 24
// TODO: Вызовите метод info() электроники
// TODO: Выведите "Warranty: [warranty] months"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий