Что такое extension
В языке программирования Dart ключевое слово extension используется для определения расширений (extension methods) — это способ добавить новые методы, геттеры или сеттеры к существующим типам, не изменяя их исходный код и не создавая подклассы. Расширения позволяют писать более чистый и выразительный код, улучшая читаемость и переиспользуемость.
📌 Основная идея
Расширения позволяют «написать метод, который как будто принадлежит» существующему классу, даже если ты не контролируешь этот класс (например, String, int, List, классы из SDK, сторонние библиотеки и т.д.).
Синтаксис:
extension ИмяРасширения on Тип {
// добавленные методы или геттеры
}
Пример:
extension StringExtension on String {
String capitalize() {
if (this.isEmpty) return this;
return '${this\[0\].toUpperCase()}${this.substring(1)}';
}
}
Теперь ты можешь вызывать метод capitalize() на любом String:
void main() {
String name = 'john';
print(name.capitalize()); // John
}
⚙️ Что можно добавить с помощью extension
-
Методы
-
Геттеры и сеттеры (в т.ч. вычисляемые)
-
Поддержку типизированного интерфейса (но не реализации)
Что нельзя:
-
Добавить поля (переменные состояния)
-
Переопределить поведение уже существующих методов (например, toString() нельзя «перезаписать» через extension)
-
Использовать приватные члены расширяемого класса (с _(нижнее_подчеркивание))
🧠 Примеры использования
Расширение для чисел
extension IntParsing on String {
int? toIntOrNull() {
return int.tryParse(this);
}
}
void main() {
print('123'.toIntOrNull()); // 123
print('abc'.toIntOrNull()); // null
}
Расширение для списков
extension ListUtils<T> on List<T> {
bool isNullOrEmpty() => this.isEmpty;
}
void main() {
List<int> numbers = \[\];
print(numbers.isNullOrEmpty()); // true
}
Расширение для BuildContext во Flutter
Очень популярное применение — добавление удобных методов к BuildContext:
extension ContextExtensions on BuildContext {
void showSnackBar(String message) {
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(content: Text(message)),
);
}
}
context.showSnackBar('Успешно сохранено!');
📦 Где extension особенно полезны
-
Flutter: чтобы избежать большого количества utils, helper и context.extension, делают работу с BuildContext, TextStyle, Theme, MediaQuery удобной и краткой.
-
Форматирование и парсинг данных (например, даты, чисел, строки).
-
Улучшение читаемости бизнес-логики, когда операции звучат как «методы» объекта (user.isAdult(), product.discountedPrice()).
-
Модульное расширение классов, когда нельзя использовать наследование (например, сторонние библиотеки).
🏷️ Именованные расширения и конфликты
Можно дать расширению имя, чтобы избежать конфликтов между разными расширениями одного типа:
extension CapitalizeExt on String {
String capitalize() => '${this\[0\].toUpperCase()}${substring(1)}';
}
extension ReverseExt on String {
String reversed() => split('').reversed.join();
}
Если в одном месте два расширения добавляют одинаковый метод, Dart не сможет выбрать нужный — будет ошибка компиляции. В этом случае можно использовать импорт с указанием имени:
import 'string_extensions.dart' show CapitalizeExt;
🔒 Ограничения
- Расширения работают только в статически типизированном контексте. Если тип переменной dynamic, расширения не применяются:
dynamic value = 'hello';
// value.capitalize(); // ошибка: метод не найден
-
Нельзя использовать this в конструкторе, потому что у расширения нет собственного состояния.
-
Нельзя использовать приватные поля или методы класса, к которому добавляется расширение — это противоречит инкапсуляции.
🤝 Extension vs Inheritance
Сравнение | Расширения (extension) | Наследование (extends) |
---|---|---|
Требует доступа к коду | Нет | Да |
--- | --- | --- |
Можно использовать с | Любым типом | Только с классами |
--- | --- | --- |
Можно переопределить | Нет | Да |
--- | --- | --- |
Можно добавить поля | Нет | Да |
--- | --- | --- |
Безопасность типов | Сохраняется | Сохраняется |
--- | --- | --- |
🧩 Extension methods и типы
Расширения могут быть обобщёнными, поддерживают generic-параметры и могут быть ограничены по типу через where.
extension IterableSum<T extends num> on Iterable<T> {
T sum() => reduce((a, b) => (a + b) as T);
}
void main() {
var ints = \[1, 2, 3\];
var doubles = \[1.5, 2.5\];
print(ints.sum()); // 6
print(doubles.sum()); // 4.0
}
Таким образом, extension в Dart — это мощный инструмент, который позволяет логически расширить функциональность существующих типов без модификации их кода и без нарушения принципов ООП. Это даёт возможность писать более выразительный, читаемый и поддерживаемый код.