Что такое 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;

🔒 Ограничения

  1. Расширения работают только в статически типизированном контексте. Если тип переменной dynamic, расширения не применяются:
dynamic value = 'hello';
// value.capitalize(); // ошибка: метод не найден
  1. Нельзя использовать this в конструкторе, потому что у расширения нет собственного состояния.

  2. Нельзя использовать приватные поля или методы класса, к которому добавляется расширение — это противоречит инкапсуляции.

🤝 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 — это мощный инструмент, который позволяет логически расширить функциональность существующих типов без модификации их кода и без нарушения принципов ООП. Это даёт возможность писать более выразительный, читаемый и поддерживаемый код.