Методы расширения
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 83 из 110.
Иногда вам нужно добавить функциональность в класс, которым вы не владеете — например, во встроенные типы Dart String или int. Вы не можете изменять их исходный код, а наследование не помогает в случае с финальными классами. Именно здесь на помощь приходят методы расширения.
Расширения позволяют добавлять новые методы к существующим типам без их изменения или создания подклассов:
extension StringExtras on String {
String reverse() {
return split('').reversed.join('');
}
bool get isPalindrome {
var cleaned = toLowerCase().replaceAll(' ', '');
return cleaned == cleaned.split('').reversed.join('');
}
}
void main() {
print('hello'.reverse()); // olleh
print('radar'.isPalindrome); // true
}Синтаксис: extension ExtensionName on Type. Внутри вы определяете методы и геттеры точно так же, как в обычном классе. После определения эти методы появляются во всех экземплярах этого типа, как если бы они были встроенными.
Расширения работают с любым типом, включая ваши собственные классы и универсальные типы:
extension IntExtras on int {
int squared() => this * this;
}
extension ListExtras<T> on List<T> {
T? get secondOrNull => length >= 2 ? this[1] : null;
}
void main() {
print(5.squared()); // 25
print([1, 2, 3].secondOrNull); // 2
}Обратите внимание, что внутри расширения this ссылается на экземпляр, для которого вызван метод. Расширения — это аккуратный способ улучшить существующие типы, сохраняя ваш код организованным и читаемым.
Задание
ЛегкоДавайте расширим встроенные типы Dart полезной функциональностью с помощью методов расширения! Вы создадите расширения, которые добавляют полезные методы к типам String и List, а затем используете их в своем основном файле.
Вы организуете свой код в два файла:
extensions.dart: Создайте два расширения, которые добавляют новые возможности к существующим типам:- Расширение под названием
StringUtilsдляStringс:- Методом
countVowels(), который возвращает количество гласных (a, e, i, o, u — без учета регистра) в строке - Геттером
initials, который возвращает первую букву каждого слова (разделенных пробелами), объединенных вместе и в верхнем регистре. Например,"hello world"превращается в"HW"
- Методом
- Расширение под названием
ListUtils<T>дляList<T>с:- Методом
safeGet(int index), который возвращает элемент по индексу, если он существует, илиnull, если индекс находится вне диапазона - Геттером
hasMultiple, который возвращаетtrue, если в списке более одного элемента
- Методом
- Расширение под названием
main.dart: Импортируйте свои расширения и продемонстрируйте их в действии:- Создайте строку
"Object Oriented Programming"и выведите количество гласных в ней в формате:Vowels: [count] - Выведите инициалы этой же строки в формате:
Initials: [initials] - Создайте список целых чисел
[10, 20, 30] - Выведите результат
safeGet(1)в формате:Element at 1: [value] - Выведите результат
safeGet(5)в формате:Element at 5: [value] - Выведите, содержит ли список несколько элементов, в формате:
Has multiple: [true/false]
- Создайте строку
Помните, что внутри расширения this относится к экземпляру, для которого вызывается метод. Ваши расширения заставят эти типы выглядеть так, будто эти методы встроены в них изначально!
Ожидаемый вывод:
Vowels: 9
Initials: OOP
Element at 1: 20
Element at 5: null
Has multiple: trueШпаргалка
Методы расширения позволяют добавлять новую функциональность к существующим типам без изменения их исходного кода или использования наследования.
Синтаксис для создания расширения — extension ExtensionName on Type:
extension StringExtras on String {
String reverse() {
return split('').reversed.join('');
}
bool get isPalindrome {
var cleaned = toLowerCase().replaceAll(' ', '');
return cleaned == cleaned.split('').reversed.join('');
}
}После определения методы расширения можно вызывать на экземплярах этого типа так, как если бы они были встроенными:
print('hello'.reverse()); // olleh
print('radar'.isPalindrome); // trueРасширения работают с любым типом, включая встроенные типы, ваши собственные классы и универсальные типы:
extension IntExtras on int {
int squared() => this * this;
}
extension ListExtras<T> on List<T> {
T? get secondOrNull => length >= 2 ? this[1] : null;
}
print(5.squared()); // 25
print([1, 2, 3].secondOrNull); // 2Внутри расширения this ссылается на экземпляр, для которого вызван метод.
Попробуйте сами
import 'extensions.dart';
void main() {
// Создать строку
String text = "Object Oriented Programming";
// TODO: Вывести количество гласных, используя countVowels()
// Формат: Vowels: [count]
// TODO: Вывести инициалы, используя геттер initials
// Формат: Initials: [initials]
// Создать список целых чисел
List<int> numbers = [10, 20, 30];
// TODO: Вывести результат safeGet(1)
// Формат: Element at 1: [value]
// TODO: Вывести результат safeGet(5)
// Формат: Element at 5: [value]
// TODO: Вывести, содержит ли список несколько элементов
// Формат: Has multiple: [true/false]
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий