Что такое Generics в Dart?

«Что такое Generics в Dart?» — вопрос из категории Dart Core, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Generics в Dart — это синтаксис и система типов, позволяющие писать код, параметризованный типами. Это основа для создания типобезопасных коллекций и утилит, которые являются краеугольным камнем языка.

Зачем они нужны? Без Generics пришлось бы использовать тип dynamic или Object, что лишает проверки типов и требует постоянных приведений (casts).

// Без Generics (небезопасно и неудобно)
class OldBox {
  final Object value;
  OldBox(this.value);
}

void main() {
  var box = OldBox('строка');
  int length = (box.value as String).length; // Требуется явное приведение типа
}

// С Generics (безопасно и удобно)
class NewBox<T> {
  final T value;
  NewBox(this.value);
}

void main() {
  var box = NewBox<String>('строка');
  int length = box.value.length; // Компилятор знает, что value — это String
}

Generics в функциях и методах:

// Функция, возвращающая первый элемент списка любого типа
T getFirst<T>(List<T> list) {
  if (list.isEmpty) throw StateError('List is empty');
  return list.first;
}

void main() {
  print(getFirst<int>([1, 2, 3])); // 1
  print(getFirst<String>(['a', 'b'])); // a
  // Тип часто выводится автоматически:
  print(getFirst([1.5, 2.7])); // 1.5, T выводится как double
}

Использование в Flutter API:

// FutureBuilder автоматически выводит тип из Future<T>
FutureBuilder<List<User>>(
  future: fetchUsers(),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      // snapshot.data имеет тип List<User>
      return ListView.builder(
        itemCount: snapshot.data!.length,
        itemBuilder: (ctx, i) => Text(snapshot.data![i].name),
      );
    }
    return CircularProgressIndicator();
  },
)