Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Моя любимая фича в Dart
Если честно, у меня несколько любимых фич, но одна выделяется особенно — это Null Safety (nullable and non-nullable types) в сочетании с Pattern Matching. Позвольте объяснить почему и как это изменило мой код.
Primary фича: Null Safety
Это революционная фича, которая была введена в Dart 2.12. Null Safety радикально улучшает качество кода, предотвращая целый класс ошибок на этапе компиляции.
Суть проблемы
В старом Dart (и других языках как JavaScript, Java) null-ошибки были частой причиной crashes:
// Старый Dart (без Null Safety)
String name; // Может быть null
name.toUpperCase(); // Может вызвать runtime ошибку!
Решение: Null Safety
// Современный Dart
String name = 'John'; // Не может быть null
name.toUpperCase(); // 100% безопасно!
String? maybeNull = null; // Явно может быть null
if (maybeNull != null) {
maybeNull.toUpperCase(); // Компилятор знает, что здесь не null
}
Почему это моя любимая фича
1. Ловит ошибки до runtime
class User {
final String name;
final String? email; // Явно может быть null
void printEmail() {
// Этот код не скомпилируется без проверки null
// print(email.toLowerCase()); // Ошибка компиляции!
// Вынуждены проверить
if (email != null) {
print(email.toLowerCase()); // OK
}
}
}
Это спасает часы отладки и предотвращает крахи в production.
2. Делает код более явным
// Сразу видно, какие значения могут быть null
class Article {
final String title; // Всегда есть
final String? summary; // Может быть null
final List<String>? tags; // Может быть null
final int? viewCount; // Может быть null
}
Это самодокументирующийся код.
3. Меньше проверок и более чистый код
// Без Null Safety требовалось много проверок
if (user != null && user.email != null && user.email.isNotEmpty) {
sendEmail(user.email);
}
// С Null Safety и pattern matching
if (user case User(email: String email) when email.isNotEmpty) {
sendEmail(email);
}
Вторая любимая фича: Pattern Matching (Dart 3.0+)
Pattern Matching в Dart стал невероятно мощным инструментом. Это больше, чем простой switch statement.
Примеры Pattern Matching
var number = 5;
switch (number) {
case 0:
print('Zero');
case < 0:
print('Negative');
case > 0:
print('Positive');
case == 5:
print('Exactly five');
}
Деструктуризация объектов
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
var point = Point(3, 4);
var distance = switch (point) {
Point(x: 0, y: 0) => 'Origin',
Point(x: var x, y: var y) when x == y => 'On diagonal',
Point(x: var x, y: var y) => 'Distance: ${(x*x + y*y).sqrt()}',
};
Работа с sealed классами
sealed class Result<T> {}
final class Success<T> extends Result<T> {
final T data;
Success(this.data);
}
final class Error extends Result<Never> {
final String message;
Error(this.message);
}
final class Loading extends Result<Never> {}
var result = Success<String>('Hello');
var message = switch (result) {
Success(:final data) => 'Got: $data',
Error(:final message) => 'Error: $message',
Loading() => 'Still loading...',
};
Guard clauses
var age = 15;
var status = switch (age) {
< 0 => 'Invalid',
< 13 => 'Child',
< 18 when age >= 16 => 'Can drive in some places',
< 18 => 'Teenager',
>= 18 => 'Adult',
};
Как эти фичи работают вместе
// Реальный пример: обработка ответа от API
sealed class ApiResponse<T> {}
final class ApiSuccess<T> extends ApiResponse<T> {
final T data;
ApiSuccess(this.data);
}
final class ApiError extends ApiResponse<Never> {
final String code;
final String message;
ApiError(this.code, this.message);
}
final class ApiLoading extends ApiResponse<Never> {}
// В UI
var response = await api.fetchUser('123');
var widget = switch (response) {
ApiSuccess(data: User(name: var name, email: String? email)) =>
UserCard(name: name, email: email),
ApiError(code: '404', message: var msg) =>
ErrorWidget('Not found: $msg'),
ApiError(:final message) =>
ErrorWidget('Error: $message'),
ApiLoading() =>
LoadingWidget(),
};
Другие люимые фичи (но менее importantes)
Spread operator
var list1 = [1, 2, 3];
var list2 = [4, 5, 6];
var combined = [...list1, ...list2]; // [1, 2, 3, 4, 5, 6]
var conditionalList = [1, 2, if (someCondition) 3];
Extension методы
extension StringUtils on String {
String capitalize() {
return this[0].toUpperCase() + this.substring(1);
}
}
var text = 'hello';
print(text.capitalize()); // Hello
Cascade operator
var person = Person()
..name = 'John'
..age = 30
..email = 'john@example.com';
// Вместо
var person = Person();
person.name = 'John';
person.age = 30;
person.email = 'john@example.com';
Type inference
var list = [1, 2, 3]; // Dart автоматически знает, что это List<int>
var map = {'name': 'John', 'age': 30}; // Map<String, dynamic>
// Не нужно писать явные типы везде
Почему я люблю именно Null Safety + Pattern Matching
Безопасность:
- Ошибки null-pointer ловятся до runtime
- Компилятор помогает писать правильный код
Читаемость:
- Pattern matching делает код очень выразительным
- Меньше вложенных if-else
- Структура кода более понятна
Производительность:
- Меньше null-проверок в runtime
- Компилятор может оптимизировать лучше
Удовлетворение:
- Когда код компилируется, он работает
- Меньше неожиданных ошибок
- Чувствуешь доверие к своему коду
Пример до и после
Без Pattern Matching (Dart 1.0 стиль)
ApiResponse response = await api.fetch();
if (response is Success) {
var successResponse = response as Success;
var data = successResponse.data;
if (data != null) {
showData(data);
} else {
showError('No data');
}
} else if (response is Error) {
var errorResponse = response as Error;
showError(errorResponse.message);
} else if (response is Loading) {
showLoading();
}
С Pattern Matching (Dart 3.0+)
var response = await api.fetch();
switch (response) {
Success(data: var data) => showData(data),
Error(:final message) => showError(message),
Loading() => showLoading(),
}
Второй вариант безопаснее, короче и понятнее.
Итог
Моя любимая фича в Dart — это Null Safety, потому что она радикально улучшает безопасность кода. А Pattern Matching идеально дополняет это, делая код более выразительным и безопасным.
Эти две фичи вместе делают Dart одним из самых удобных языков для разработки, где ошибки ловятся рано, а код остаётся чистым и понятным.