Null Safety
Tipe nullable (?), operator ?., ??, ??=, !, late, pengecekan null dan promosi tipe, serta cara menulis kode bebas NullPointer.
Masalah "miliaran dolar"
Penemu konsep null menyebutnya "kesalahan miliaran dolar": program crash karena memakai nilai yang ternyata kosong. Dart menyelesaikannya dengan sound null safety: compiler menolak kode yang berpotensi memakai null secara keliru.
Aturan dasarnya
String nama = 'Budi';
// nama = null; // ERROR: String tidak boleh null
String? alias = 'Bud'; // tanda ? = boleh null
alias = null; // OK
Tipe tanpa ? tidak pernah null. Tipe dengan ? boleh null, dan Dart memaksamu menanganinya sebelum dipakai.
String? alias;
// print(alias.length); // ERROR: alias mungkin null
Cara menangani nullable
| Operator | Arti | Contoh |
|---|---|---|
?. | Akses aman; hasilnya null bila objek null | alias?.length |
?? | Nilai cadangan bila null | alias ?? 'Tamu' |
??= | Isi hanya bila masih null | alias ??= 'Tamu' |
! | "Aku yakin ini tidak null" (berbahaya) | alias!.length |
?[ ] | Indeks aman | daftar?[0] |
...? | Spread aman | [...?lain] |
String? alias;
print(alias?.length); // null (tidak crash)
print(alias ?? 'Tamu'); // Tamu
print(alias?.toUpperCase() ?? '-'); // -
Pengecekan null dan type promotion
Setelah kamu memeriksa null dengan if, Dart tahu variabel itu aman di dalam blok:
void cetak(String? teks) {
if (teks == null) {
print('kosong');
return;
}
print(teks.length); // OK: di sini teks pasti String
}
// Pola if-case (Dart 3)
if (alias case final a?) {
print(a.length); // a bertipe String
}
Promosi tipe hanya bekerja untuk variabel lokal dan final. Untuk field class, salin dulu ke variabel lokal.
Operator ! — gunakan dengan hemat
String? x = ambilNama();
print(x!.length); // crash dengan error jika x ternyata null
! memindahkan pemeriksaan dari compiler ke waktu program jalan. Lebih baik pakai ?? atau if. Gunakan ! hanya jika logika menjamin nilainya ada.
late: diisi nanti
late String judul; // non-null, tapi diisi setelah deklarasi
void muat() {
judul = 'Dart';
}
late final String config = bacaKonfigurasi(); // late + inisialisasi malas:
// dihitung hanya saat pertama dipakai
Jika late dibaca sebelum diisi, terjadi LateInitializationError.
required dan null
class Pengguna {
final String nama; // wajib ada
final String? email; // opsional
Pengguna({required this.nama, this.email});
}
Pola ini menjadikan data wajib dan opsional terlihat jelas dari tipenya.
Nullable di collection
List<int?> a = [1, null, 3]; // elemen boleh null
List<int>? b; // listnya yang boleh null
Map<String, int> stok = {'a': 1};
int jumlah = stok['b'] ?? 0; // hasil lookup Map selalu nullable
Ringkasan kebiasaan baik
- Jadikan tipe non-null sebagai default; pakai
?hanya bila "tidak ada nilai" memang masuk akal. - Selesaikan null sedini mungkin dengan
??atau return awal. - Hindari
!dandynamic.
Error umum
The property 'length' can't be unconditionally accessed because the receiver can be 'null'.→ gunakan?.atau cek null dulu.Null check operator used on a null value→!dipakai pada nilai null.LateInitializationError→ variabellatedibaca sebelum diisi.