throw în Dart

Excepții Dart 1.0

Aruncă o excepție sau o eroare care oprește execuția normală până o prinde un catch.

Sintaxă
throw Exception('mesaj');
throw ArgumentError('mesaj');
throw ExceptiaMea(...);
Tip f() => throw UnimplementedError();

Ce face throw

throw oprește funcția curentă și „aruncă” un obiect în sus, prin funcțiile care au apelat-o, până îl prinde un try-catch. Dacă nu îl prinde nimeni, programul se oprește și afișează mesajul și stiva de apeluri.

Dart face diferența între Exception (situații așteptate, pe care programul le poate trata: fișier lipsă, format greșit) și Error (greșeli de programare: ArgumentError, StateError, RangeError). Poți arunca orice obiect nenul, dar convenția este să arunci clase care implementează Exception sau extind Error.

throw este o expresie, deci poate sta după => sau după ??: final nume = argument ?? (throw ArgumentError('lipsește numele'));.

Exemple

Exemplul 1: Validarea unui argument

Funcția aruncă ArgumentError pentru o sumă negativă; în main o prindem.

double aplicaReducere(double pret, double procent) {
  if (procent < 0 || procent > 100) {
    throw ArgumentError('procent invalid: $procent');
  }
  return pret * (1 - procent / 100);
}

void main() {
  print(aplicaReducere(200, 15));
  try {
    aplicaReducere(200, 120);
  } on ArgumentError catch (e) {
    print('Eroare: ${e.message}');
  }
}

Rezultat

170.0
Eroare: procent invalid: 120.0

Exemplul 2: Excepție proprie

O clasă care implementează Exception, cu un toString clar.

class StocInsuficient implements Exception {
  final String produs;
  final int cerut, disponibil;
  StocInsuficient(this.produs, this.cerut, this.disponibil);

  @override
  String toString() => 'StocInsuficient: $produs (cerut $cerut, în stoc $disponibil)';
}

final stoc = {'mere': 10, 'pere': 2};

void comanda(String produs, int bucati) {
  final disponibil = stoc[produs] ?? 0;
  if (bucati > disponibil) throw StocInsuficient(produs, bucati, disponibil);
  stoc[produs] = disponibil - bucati;
  print('Comandă acceptată: $bucati $produs');
}

void main() {
  for (final (p, n) in [('mere', 3), ('pere', 5)]) {
    try {
      comanda(p, n);
    } on StocInsuficient catch (e) {
      print(e);
    }
  }
}

Rezultat

Comandă acceptată: 3 mere
StocInsuficient: pere (cerut 5, în stoc 2)

Exemplul 3: throw ca expresie

throw stă după ?? și după =>.

String cere(Map<String, String> config, String cheie) =>
    config[cheie] ?? (throw StateError('lipsește setarea "$cheie"'));

void main() {
  final config = {'oras': 'Oradea'};
  print(cere(config, 'oras'));
  try {
    cere(config, 'port');
  } on StateError catch (e) {
    print(e.message);
  }
}

Rezultat

Oradea
lipsește setarea "port"

Greșeli frecvente

  • throw 'text';: este permis, dar un String nu are tip specific de prins și nici stivă clară. Aruncă o clasă de excepție.
  • Să prinzi Error-uri (greșeli de programare) ca pe excepții normale: de obicei trebuie reparat codul, nu ascunsă eroarea.
  • Pentru a arunca mai departe excepția prinsă, folosește rethrow, nu throw e: păstrezi stiva originală.

Comenzi înrudite

  • try, catch și finally: try prinde excepțiile din bloc; on alege tipul, catch dă acces la obiect, finally rulează mereu.
  • rethrow: Aruncă mai departe excepția prinsă în catch, păstrând stiva de apeluri originală.
  • assert: Verifică o condiție în timpul dezvoltării; în producție și la dart run implicit, assert este ignorat.