Наследование класса Extend a class
Используйте ключевое слово extends для создания подкласса на основе суперкласса (родительского класса).
Ключевое слово super позволяет вызывать члены родительского класса (методы, конструкторы и т.д.) из подкласса.
class Television {
void turnOn() {
...
// Код, выполняющийся в методе родительского класса
}
}
// SmartTelevision — подкласс, Television — родительский класс
class SmartTelevision extends Television {
@override
void turnOn() {
super.turnOn(); // Вызов метода turnOn родительского класса
// Уникальный код для подкласса
...
}
}Code language: Dart (dart)
Пример: обычный телевизор выступает как родительский класс, а смарт-телевизор — как подкласс. Смарт-телевизор наследует возможности обычного телевизора.
extends имеет и другой вариант применения: работа с параметризованными типами из главы о дженериках.
Практический пример
class Television {
void turnOn() {
print("Включить телевизор, активировать базовый вывод изображения");
}
}
// SmartTelevision — подкласс, Television — родительский класс
class SmartTelevision extends Television {
@override
void turnOn() {
super.turnOn(); // Вызов метода turnOn родительского класса
// Уникальный код для подкласса
print("Смарт-телевизор: активировать сетевой модуль, загрузить главный экран интеллектуальной системы");
}
}
void main() {
// Экземпляр обычного телевизора
Television normalTv = Television();
print("===== Включение обычного телевизора =====");
normalTv.turnOn();
// Экземпляр смарт-телевизора
SmartTelevision smartTv = SmartTelevision();
print("\n===== Включение смарт-телевизора =====");
smartTv.turnOn();
}Code language: Dart (dart)
Результат выполнения
D:\dartdemo\firstdart>dart run
Building package executable... (1.4s)
Built firstdart:firstdart.
===== Включение обычного телевизора =====
Включить телевизор, активировать базовый вывод изображения
===== Включение смарт-телевизора =====
Включить телевизор, активировать базовый вывод изображения
Переопределение членов Overriding members
1. Переопределяемые элементы
Подкласс может переопределять: методы экземпляра (в том числе операторы), геттеры и сеттеры.
2. Аннотация @override
Назначение: явно указать, что метод намеренно переопределяет член родительского класса и позволить компилятору выполнять проверки.
class Television {
set contrast(int value) {}
}
class SmartTelevision extends Television {
@override // Сообщает компилятору, что этот метод переопределяет метод родительского класса
set contrast(num value) {}
}
Code language: JavaScript (javascript)
3. Правила корректного переопределения
- Возвращаемый тип: возвращаемый тип переопределяющего метода должен совпадать с типом родительского метода или быть его подтипом.
- Тип параметра: тип параметра переопределяющего метода должен совпадать с типом родительского метода или быть его надтипом. (Пример:
int→num. num является надтипом int, поэтому это разрешено.) - Количество позиционных параметров: если исходный метод имеет n позиционных параметров, переопределяющий метод также должен объявить ровно n позиционных параметров.
- Ограничения дженериков: обобщённый метод не может переопределять необобщённый метод и наоборот.
4. Ключевое слово covariant (особый случай сужения типа)
Стандартные правила запрещают сужать тип параметра (нисходящее приведение типов может вызвать ошибки во время выполнения) ;
Если разработчик гарантирует отсутствие ошибок типов, пометьте параметр ключевым словом covariant, чтобы разрешить сужение типа параметра.
При переопределении оператора == вы также обязательно должны переопределить геттер hashCode, унаследованный от Object.
noSuchMethod ()
1. Назначение
Когда код пытается вызвать несуществующий метод экземпляра / переменную экземпляра, можно переопределить noSuchMethod() для перехвата вызова и реализации собственной логики.
Без переопределения доступ к отсутствующему члену сразу генерирует ошибку NoSuchMethodError.
class A {
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ${invocation.memberName}');
}
}
Code language: JavaScript (javascript)
- Объект
Invocation: хранит всю информацию о вызове (имя члена, параметры и т.д.)
2. Два условия вызова нереализованных методов (достаточно одного)
- Статический тип объекта-получателя вызова —
dynamic; - Статический тип получателя объявляет этот нереализованный метод (он может быть абстрактным), а динамический тип получателя имеет свою реализацию
noSuchMethod(), отличную от стандартной версии изObject.
Пример
class A {
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ${invocation.memberName}');
// Дополнительный вывод параметров и типа вызова для полной информации
print('Параметры вызова:${invocation.arguments}');
print('Тип вызова:${invocation.runtimeType}');
}
}
void main() {
A obj = A();
// Вызов несуществующего метода активирует noSuchMethod
obj.hello("test", 123);
// Обращение к несуществующему свойству
obj.unknownValue;
}Code language: Dart (dart)
D:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:6:30: Error: The getter 'arguments' isn't defined for the type 'Invocation'.
- 'Invocation' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'arguments'.
print('cal:${invocation.arguments}');
^^^^^^^^^
bin/firstdart.dart:14:7: Error: The method 'hello' isn't defined for the type 'A'.
- 'A' is from 'bin/firstdart.dart'.
Try correcting the name to the name of an existing method, or defining a method named 'hello'.
obj.hello("test", 123);
^^^^^
bin/firstdart.dart:16:7: Error: The getter 'unknownValue' isn't defined for the type 'A'.
- 'A' is from 'bin/firstdart.dart'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'unknownValue'.
obj.unknownValue;Code language: PHP (php)
Наследование классов и переопределение членов