Spread и Rest операторы
Введение
Spread (`...`) и Rest операторы - это мощные инструменты в Dart, которые значительно упрощают работу с коллекциями. Они позволяют "разворачивать" элементы одной коллекции в другую, создавать копии, объединять коллекции и многое другое.
1. Spread оператор (`...`)
Spread оператор позволяет "развернуть" элементы коллекции в другую коллекцию.
1.1 Базовый синтаксис
Dart - Объединение списков
List<int> numbers1 = [1, 2, 3];
List<int> numbers2 = [4, 5, 6];
// Объединение списков с помощью spread оператора
List<int> combined = [...numbers1, ...numbers2];
print(combined); // [1, 2, 3, 4, 5, 6]
1.2 Создание копий коллекций
Dart - Копирование списка
// Создание поверхностной копии списка
List<String> original = ['a', 'b', 'c'];
List<String> copy = [...original];
// Изменение копии не влияет на оригинал
copy.add('d');
print('Оригинал: $original'); // [a, b, c]
print('Копия: $copy'); // [a, b, c, d]
1.3 Добавление элементов при создании
Dart - Добавление элементов
List<int> base = [2, 3, 4];
List<int> extended = [1, ...base, 5, 6];
print(extended); // [1, 2, 3, 4, 5, 6]
// Можно добавлять элементы в любое место
List<String> fruits = ['яблоко', 'банан'];
List<String> menu = ['завтрак:', ...fruits, 'обед:', 'суп', 'салат'];
print(menu); // [завтрак:, яблоко, банан, обед:, суп, салат]
2. Null-aware Spread оператор (`...?`)
Этот оператор безопасно работает с nullable коллекциями.
2.1 Базовое использование
Dart - Null-aware Spread
List<int>? nullableList = null;
List<int> numbers = [1, 2];
// Без null-aware оператора было бы исключение
List<int> safe = [...numbers, ...?nullableList, 3];
print(safe); // [1, 2, 3]
// Если список не null, элементы добавляются
nullableList = [10, 20];
List<int> withValues = [...numbers, ...?nullableList, 3];
print(withValues); // [1, 2, 10, 20, 3]
2.2 Практический пример с API данными
Dart - API Response
class ApiResponse {
List<String>? data;
List<String>? errors;
ApiResponse({this.data, this.errors});
}
void processApiResponse(ApiResponse response) {
// Безопасное объединение данных и ошибок
List<String> allMessages = [
'Обработка запроса:',
...?response.data,
'Ошибки:',
...?response.errors,
'Обработка завершена'
];
print(allMessages);
}
// Тестирование
var response1 = ApiResponse(data: ['Данные получены', 'Валидация пройдена']);
var response2 = ApiResponse(errors: ['Ошибка сети', 'Таймаут']);
var response3 = ApiResponse(); // null данные
processApiResponse(response1);
processApiResponse(response2);
processApiResponse(response3);
3. Spread с разными типами коллекций
3.1 Работа с Set
Dart - Spread с Set
Set<int> set1 = {1, 2, 3};
Set<int> set2 = {3, 4, 5};
// Объединение множеств (дубликаты автоматически удаляются)
Set<int> combinedSet = {...set1, ...set2};
print(combinedSet); // {1, 2, 3, 4, 5}
// Преобразование в список
List<int> listFromSets = [...set1, ...set2];
print(listFromSets); // [1, 2, 3, 3, 4, 5] - дубликаты сохраняются
3.2 Работа с Map
Dart - Spread с Map
Map<String, int> scores1 = {'Анна': 95, 'Борис': 87};
Map<String, int> scores2 = {'Вера': 92, 'Денис': 88};
// Объединение карт
Map<String, int> allScores = {...scores1, ...scores2};
print(allScores); // {Анна: 95, Борис: 87, Вера: 92, Денис: 88}
// При совпадении ключей последнее значение побеждает
Map<String, int> updated = {...scores1, 'Анна': 98, ...scores2};
print(updated['Анна']); // 98
4. Практические примеры использования
4.1 Создание конфигурационных объектов
Dart - Конфигурация БД
class DatabaseConfig {
final Map<String, dynamic> config;
DatabaseConfig({required this.config});
// Создание конфигурации с базовыми настройками
factory DatabaseConfig.withDefaults(Map<String, dynamic> userConfig) {
const defaultConfig = {
'host': 'localhost',
'port': 5432,
'timeout': 30,
'ssl': false,
};
return DatabaseConfig(
config: {...defaultConfig, ...userConfig}
);
}
}
// Использование
var dbConfig = DatabaseConfig.withDefaults({
'host': 'production-db.com',
'ssl': true,
'username': 'admin'
});
print(dbConfig.config);
// {host: production-db.com, port: 5432, timeout: 30, ssl: true, username: admin}
4.2 Объединение результатов поиска
Dart - Сервис поиска
class SearchService {
List<String> searchInDatabase(String query) {
// Имитация поиска в БД
return ['db_result_1', 'db_result_2'];
}
List<String> searchInCache(String query) {
// Имитация поиска в кэше
return ['cache_result_1'];
}
List<String>? searchInExternal(String query) {
// Может вернуть null если внешний сервис недоступен
return query.length > 5 ? ['external_result'] : null;
}
List<String> performSearch(String query) {
return [
'Результаты поиска для: $query',
...searchInCache(query),
...searchInDatabase(query),
...?searchInExternal(query), // Безопасное добавление
'Поиск завершен'
];
}
}
// Тестирование
var searchService = SearchService();
print(searchService.performSearch('dart'));
print(searchService.performSearch('programming'));
4.3 Построение меню приложения
Dart - Построитель меню
class MenuItem {
final String title;
final String? icon;
MenuItem(this.title, {this.icon});
@override
String toString() => icon != null ? '$icon $title' : title;
}
class MenuBuilder {
static List<MenuItem> buildMainMenu({
required bool isAdmin,
required bool isLoggedIn,
List<MenuItem>? customItems
}) {
// Базовые элементы меню
const baseItems = [
MenuItem('Главная', icon: '🏠'),
MenuItem('О нас', icon: 'ℹ️'),
];
// Элементы для авторизованных пользователей
const userItems = [
MenuItem('Профиль', icon: '👤'),
MenuItem('Настройки', icon: '⚙️'),
];
// Элементы для администраторов
const adminItems = [
MenuItem('Панель администратора', icon: '🛠️'),
MenuItem('Пользователи', icon: '👥'),
];
return [
...baseItems,
if (isLoggedIn) ...userItems,
if (isAdmin) ...adminItems,
...?customItems, // Пользовательские элементы (могут быть null)
MenuItem('Выход', icon: '🚪'),
];
}
}
// Использование
print('Меню для гостя:');
var guestMenu = MenuBuilder.buildMainMenu(isAdmin: false, isLoggedIn: false);
guestMenu.forEach(print);
print('\nМеню для пользователя:');
var userMenu = MenuBuilder.buildMainMenu(isAdmin: false, isLoggedIn: true);
userMenu.forEach(print);
print('\nМеню для администратора:');
var adminMenu = MenuBuilder.buildMainMenu(
isAdmin: true,
isLoggedIn: true,
customItems: [MenuItem('Отчеты', icon: '📊'), MenuItem('Логи', icon: '📋')]
);
adminMenu.forEach(print);
5. Spread с условными выражениями
5.1 Условное добавление элементов
Dart - Список покупок
List<String> buildShoppingList({
required bool needMilk,
required bool needBread,
required bool isWeekend,
List<String>? specialItems
}) {
return [
'Список покупок:',
'- Яйца',
if (needMilk) '- Молоко',
if (needBread) '- Хлеб',
if (isWeekend) ...['- Пицца', '- Мороженое'], // Разворачиваем список
...?specialItems,
];
}
// Тестирование
print(buildShoppingList(
needMilk: true,
needBread: false,
isWeekend: true,
specialItems: ['- Торт', '- Свечи']
));
5.2 Фильтрация при объединении
Dart - Фильтрация чисел
List<int> combinePositiveNumbers(List<List<int>> numberLists) {
List<int> result = [];
for (var list in numberLists) {
// Добавляем только положительные числа
result.addAll([
...list.where((n) => n > 0)
]);
}
return result;
}
// Альтернативный способ с использованием expand
List<int> combinePositiveNumbersV2(List<List<int>> numberLists) {
return [
...numberLists.expand((list) => list.where((n) => n > 0))
];
}
// Тестирование
var lists = [
[1, -2, 3],
[-4, 5, 6],
[7, -8, -9, 10]
];
print(combinePositiveNumbers(lists)); // [1, 3, 5, 6, 7, 10]
print(combinePositiveNumbersV2(lists)); // [1, 3, 5, 6, 7, 10]