?. și ?.. în Dart

Accesul condiționat: ?. apelează o metodă doar dacă obiectul nu este null, altfel dă null.

Sintaxă
obiect?.metoda()        // null dacă obiect este null
obiect?.camp
lista?[0]
obiect?..a = 1..b = 2   // cascadă doar dacă obiect nu este null

Ce face ?. și ?..

a?.b înseamnă: dacă a este null, rezultatul este null și b nu se mai evaluează; altfel este a.b. Așa poți merge pe un lanț de obiecte care pot lipsi fără să scrii multe if-uri: elev?.adresa?.oras. Rezultatul este mereu de tip nullabil.

Varianta pentru index este lista?[0]. Varianta pentru cascadă, ?.., execută toată cascada doar dacă obiectul nu este null: buffer?..write('a')..write('b').

Se combină des cu ??: elev?.adresa?.oras ?? 'necunoscut'. Dacă obiectul nu poate fi null, analizorul semnalează ?. ca inutil.

Exemple

Exemplul 1: Lanț de câmpuri care pot lipsi

Al doilea elev nu are adresă, deci ?. oprește lanțul și dă null; ?? pune textul implicit.

class Adresa {
  final String oras;
  Adresa(this.oras);
}

class Elev {
  final String nume;
  final Adresa? adresa;
  Elev(this.nume, [this.adresa]);
}

void main() {
  final elevi = [Elev('Ana', Adresa('Iași')), Elev('Mihai')];
  for (final e in elevi) {
    final oras = e.adresa?.oras;
    print('${e.nume}: ${oras ?? 'adresă necunoscută'} (lungime: ${oras?.length})');
  }
}

Rezultat

Ana: Iași (lungime: 4)
Mihai: adresă necunoscută (lungime: null)

Exemplul 2: Index condiționat ?[]

note?[0] citește primul element doar dacă lista există.

List<int>? noteleLui(String elev) => elev == 'Ana' ? [10, 9] : null;

void main() {
  for (final elev in ['Ana', 'Dan']) {
    final prima = noteleLui(elev)?[0];
    print('$elev: prima notă = ${prima ?? '-'}');
  }
}

Rezultat

Ana: prima notă = 10
Dan: prima notă = -

Exemplul 3: Cascadă condiționată ?..

Cascada se aplică doar pe bufferul care există; pentru null nu se întâmplă nimic.

StringBuffer? creeaza(bool activ) => activ ? StringBuffer() : null;

void main() {
  for (final activ in [true, false]) {
    final sb = creeaza(activ)
      ?..write('Comandă: ')
      ..write('3 caiete');
    print(sb?.toString() ?? 'fără buffer');
  }
}

Rezultat

Comandă: 3 caiete
fără buffer

Greșeli frecvente

  • ?. pe un obiect care nu poate fi null: avertismentul The receiver can't be null, so the null-aware operator '?.' is unnecessary.
  • Să uiți că rezultatul lui ?. este nullabil: int n = text?.length; nu compilează; adaugă ?? 0.
  • ?.. doar la prima operație: restul cascadei se scrie cu .. simplu, toată cascada este sărită când obiectul e null.

Comenzi înrudite

  • null safety (? ! ?? ??=): Tipurile nu pot fi null decât dacă au ?; operatorii !, ?? și ??= lucrează cu valorile care pot lipsi.
  • cascade (..): Operatorul .. face mai multe operații pe același obiect, una după alta, fără să repeți numele lui.
  • Object?: Tipul de bază al tuturor valorilor din Dart, inclusiv null; varianta sigură a lui dynamic.