generics în Dart

Tipuri, generics, records și pattern-uri Dart 1.0 (metode generice din Dart 1.21)

Clase și funcții cu tipuri ca parametri (<T>), care funcționează la fel pentru orice tip, păstrând verificarea tipurilor.

Sintaxă
class Cutie<T> { T continut; ... }
T primul<T>(List<T> lista) => lista.first;
class Lista<T extends num> { ... }     // T trebuie să fie num sau subtip

List<String>, Map<String, int>, Future<void>

Ce face generics

Generics (tipuri generice) îți permit să scrii o clasă sau o funcție o singură dată și s-o folosești cu mai multe tipuri. List<String> și List<int> folosesc aceeași clasă List<E>, dar analizorul știe exact ce conține fiecare și oprește note.add('zece').

Parametrul de tip se scrie între < >, de obicei cu o literă mare: T, E (element), K, V (cheie, valoare). Cu T extends num îl restrângi: atunci în cod poți folosi metodele lui num pe valori de tip T.

În Dart generics sunt păstrate la rulare (reified): lista is List<int> chiar verifică tipul elementelor declarat. Dacă nu scrii tipul, Dart îl deduce: var orase = ['Iași']; este List<String>.

Exemple

Exemplul 1: O clasă generică

Pereche<A, B> ține două valori de orice tipuri; tipurile sunt deduse din argumente.

class Pereche<A, B> {
  final A prima;
  final B aDoua;
  Pereche(this.prima, this.aDoua);

  Pereche<B, A> inversata() => Pereche(aDoua, prima);

  @override
  String toString() => '($prima, $aDoua)';
}

void main() {
  final p = Pereche('Ana', 10);
  print(p);
  print(p.inversata());
  print(p.runtimeType);
  print(Pereche<String, double>('Cluj', 4.5).runtimeType);
}

Rezultat

(Ana, 10)
(10, Ana)
Pereche<String, int>
Pereche<String, double>

Exemplul 2: Funcții generice și restricții

maxim merge pentru orice tip comparabil; suma cere numere (T extends num).

T maxim<T extends Comparable<T>>(List<T> valori) {
  var cel = valori.first;
  for (final v in valori) {
    if (v.compareTo(cel) > 0) cel = v;
  }
  return cel;
}

num suma<T extends num>(List<T> valori) => valori.fold<num>(0, (s, v) => s + v);

void main() {
  print(maxim([7, 10, 9]));
  print(maxim(['Brașov', 'Arad', 'Suceava']));
  print(suma([1.5, 2.5, 3]));
  print(suma<int>([4, 5, 6]));
}

Rezultat

10
Suceava
7.0
15

Exemplul 3: Tipuri păstrate la rulare

O listă goală declarată <int> rămâne List<int> și refuză alte valori chiar și prin dynamic.

void main() {
  final note = <int>[];
  final Object o = note;
  print(o is List<int>);
  print(o is List<String>);

  final dynamic d = note;
  try {
    d.add('zece');
  } on TypeError {
    print('Nu pot adăuga un text într-o List<int>.');
  }
}

Rezultat

true
false
Nu pot adăuga un text într-o List<int>.

Greșeli frecvente

  • Liste fără tip, [], pe care le umpli mai târziu: devin List<dynamic>. Scrie <String>[].
  • Folosirea metodelor lui num pe un T fără restricție: eroare. Adaugă T extends num.
  • Să crezi că List<int> este subtip al lui List<num> „în siguranță”: Dart permite atribuirea (covarianță), dar adăugarea unui double eșuează la rulare.

Comenzi înrudite

  • typedef: Dă un nume (alias) unui tip, de obicei unui tip de funcție sau unui tip generic lung.
  • class: Definește un tip nou de obiect, cu câmpuri (date) și metode (comportament).
  • covariant: Permite unei metode suprascrise să ceară un parametru de un tip mai restrâns decât în clasa părinte.
  • records: Grupuri de valori anonime și imutabile, de forma (a, b) sau (nume: a), ideale pentru a întoarce mai multe valori.