Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Generics и их связь с полиморфизмом в Dart
Generics (Обобщённое программирование) — это механизм, позволяющий создавать код, который работает с различными типами, сохраняя типобезопасность. Это форма параметрического полиморфизма, которая тесно связана с полиморфизмом в целом.
Что такое Generics
Generics позволяют определять классы, интерфейсы и функции, работающие с типами как с параметрами.
// Простой пример Generic класса
class Box<T> {
T _value;
Box(this._value);
T get value => _value;
void set value(T newValue) {
_value = newValue;
}
}
void main() {
// Box для String
final stringBox = Box<String>("Hello");
print(stringBox.value); // Hello
// Box для int
final intBox = Box<int>(42);
print(intBox.value); // 42
// Box для Custom класса
final userBox = Box<User>(User("John", 30));
print(userBox.value.name); // John
// Ошибка типа на этапе компиляции
// stringBox.value = 123; // Ошибка: int не совместим с String
}
Связь между Generics и полиморфизмом
Generics — это форма полиморфизма, называемая параметрическим полиморфизмом. Основные типы полиморфизма:
// 1. ПАРАМЕТРИЧЕСКИЙ ПОЛИМОРФИЗМ (Generics)
// Одна функция работает с разными типами
T identity<T>(T value) => value;
print(identity<int>(42)); // 42
print(identity<String>("hello")); // hello
print(identity<bool>(true)); // true
// 2. ПОДТИПНЫЙ ПОЛИМОРФИЗМ (Inheritance & Interface)
// Разные реализации одного интерфейса
abstract class Animal {
void makeSound();
}
class Dog implements Animal {
@override
void makeSound() => print("Woof!");
}
class Cat implements Animal {
@override
void makeSound() => print("Meow!");
}
void playWithAnimal(Animal animal) {
animal.makeSound();
}
playWithAnimal(Dog()); // Woof!
playWithAnimal(Cat()); // Meow!
Generic классы и наследование
// Generic базовый класс
class Repository<T> {
List<T> _items = [];
void add(T item) => _items.add(item);
T? getById(int index) => index < _items.length ? _items[index] : null;
List<T> getAll() => List.unmodifiable(_items);
}
// Специализированный репозиторий через наследование
class UserRepository extends Repository<User> {
User? findByEmail(String email) {
return getAll().firstWhere(
(user) => user.email == email,
orElse: () => throw Exception("User not found"),
);
}
}
class PostRepository extends Repository<Post> {
List<Post> findByAuthor(String authorId) {
return getAll()
.where((post) => post.authorId == authorId)
.toList();
}
}
void main() {
final userRepo = UserRepository();
userRepo.add(User("John", "john@example.com"));
final postRepo = PostRepository();
postRepo.add(Post("Hello", "1"));
}
Generic функции и методы
// Generic функция
T findMax<T extends Comparable<T>>(List<T> items) {
return items.reduce((a, b) => a.compareTo(b) > 0 ? a : b);
}
print(findMax<int>([1, 5, 3, 9, 2])); // 9
print(findMax<String>(["apple", "zebra", "banana"])); // zebra
// Generic метод
class Sorter {
List<T> sort<T extends Comparable<T>>(List<T> items) {
final sorted = [...items];
sorted.sort();
return sorted;
}
}
final sorter = Sorter();
print(sorter.sort<int>([3, 1, 4, 1, 5])); // [1, 1, 3, 4, 5]
Ограничения типов (Type Bounds)
// Ограничение: T должен быть наследником Comparable
T maximum<T extends Comparable<T>>(List<T> list) {
return list.reduce((a, b) => a.compareTo(b) > 0 ? a : b);
}
// Ограничение: T должен быть наследником Animal
class Zoo<T extends Animal> {
List<T> animals = [];
void addAnimal(T animal) {
animals.add(animal);
}
void feedAll() {
for (final animal in animals) {
animal.eat(); // T гарантировано имеет метод eat()
}
}
}
class Dog extends Animal {
@override
void eat() => print("Dog eating");
}
void main() {
final dogZoo = Zoo<Dog>();
dogZoo.addAnimal(Dog());
dogZoo.feedAll(); // Dog eating
}
Variance в Generics
// Covariance (ковариантность): стрелка совпадает
// Если Dog является Animal, то List<Dog> может быть List<Animal>
abstract class Container<out T> {
T get();
}
class DogContainer implements Container<Dog> {
@override
Dog get() => Dog();
}
void consumeContainer(Container<Animal> container) {
Animal animal = container.get();
animal.makeSound();
}
void main() {
consumeContainer(DogContainer()); // OK благодаря covariance
}
// Contravariance (контравариантность): стрелка противоположна
abstract class Producer<in T> {
void produce(T item);
}
class AnimalProducer implements Producer<Animal> {
@override
void produce(Animal animal) {
print("Produced animal");
}
}
void provideToProducer(Producer<Dog> producer) {
// producer.produce(Dog());
}
void main() {
// provideToProducer(AnimalProducer()); // ERROR: contravariance
}
Практические примеры во Flutter
Generic API клиент:
class ApiClient {
Future<T> get<T>(String url, T Function(Map<String, dynamic>) fromJson) async {
final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
throw Exception("API Error: ${response.statusCode}");
}
final json = jsonDecode(response.body) as Map<String, dynamic>;
return fromJson(json);
}
Future<List<T>> getList<T>(
String url,
T Function(Map<String, dynamic>) fromJson,
) async {
final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
throw Exception("API Error: ${response.statusCode}");
}
final json = jsonDecode(response.body) as List;
return json.map((item) => fromJson(item as Map<String, dynamic>)).toList();
}
}
class User {
final String id, name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json["id"],
name: json["name"],
);
}
}
void main() async {
final client = ApiClient();
final user = await client.get<User>(
"https://api.example.com/users/1",
User.fromJson,
);
print(user.name);
final users = await client.getList<User>(
"https://api.example.com/users",
User.fromJson,
);
print(users.length);
}
Generic State Management:
abstract class Repository<T> {
Future<T> fetch();
Future<void> save(T data);
}
class AsyncValueNotifier<T> extends ValueNotifier<AsyncValue<T>> {
AsyncValueNotifier(this.repository)
: super(const AsyncValue.loading());
final Repository<T> repository;
Future<void> load() async {
value = const AsyncValue.loading();
try {
final data = await repository.fetch();
value = AsyncValue.data(data);
} catch (e, st) {
value = AsyncValue.error(e, st);
}
}
}
class AsyncValue<T> {
const AsyncValue();
const factory AsyncValue.loading() = _LoadingAsyncValue;
const factory AsyncValue.data(T data) = _DataAsyncValue;
const factory AsyncValue.error(Object error, StackTrace stackTrace) =
_ErrorAsyncValue;
}
class _LoadingAsyncValue<T> extends AsyncValue<T> {
const _LoadingAsyncValue();
}
class _DataAsyncValue<T> extends AsyncValue<T> {
final T data;
const _DataAsyncValue(this.data);
}
class _ErrorAsyncValue<T> extends AsyncValue<T> {
final Object error;
final StackTrace stackTrace;
const _ErrorAsyncValue(this.error, this.stackTrace);
}
Преимущества Generics
✅ Типобезопасность — ошибки типов выявляются на этапе компиляции ✅ Переиспользование кода — одна реализация для разных типов ✅ Производительность — избегаем боксинга/анбоксинга ✅ Читаемость — явно видна работа с типами ✅ Полиморфизм — достигаем гибкости без потери типобезопасности
Заключение
Generics — это мощный инструмент для создания гибкого и типобезопасного кода. Они реализуют параметрический полиморфизм, позволяя писать код, который работает с разными типами, сохраняя при этом строгую типизацию. Правильное использование Generics делает Flutter приложения более надёжными и легче поддерживаемыми.