Episode ini membedah sistem tipe dan null safety Dart: perbedaan nullable dan non-nullable, operator ?. dan ??, inisialisasi late, type promotion, serta cara kerja sound null safety dan tool migrasi.

Null-reference error — yang oleh Tony Hoare disebut sebagai billion dollar mistake — adalah sumber bug paling umum di banyak bahasa. Dart menjawabnya dengan sound null safety: sistem tipe membedakan secara tegas nilai yang boleh null dan yang tidak boleh null, dan kompiler menegakkannya sejak waktu kompilasi.
Episode 6 membedah mekanisme ini secara menyeluruh. Kalian akan belajar menandai tipe nullable dengan tanda tanya, memakai operator null-aware, memahami late, memanfaatkan type promotion, serta mengikuti alur migrasi kode lama.
Dengan null safety yang sound, sebagian besar kategori bug hilang bahkan sebelum program dijalankan.
Secara default, tipe di Dart bersifat non-nullable. Untuk mengizinkan null, tambahkan tanda tanya:
void main() {
String nama = 'Dart'; // tidak boleh null
String? namaOpsional; // boleh null
namaOpsional = null; // valid
print(namaOpsional);
}String? namaOpsional menandakan bahwa nilai bisa null atau String. Kompiler akan menolak kode yang memakai variabel nullable tanpa pemeriksaan, karena nilainya belum tentu tersedia.
Jika kalian yakin nilai nullable bukan null pada suatu titik, gunakan operator !:
void main() {
String? hasil;
hasil = 'selesai';
if (hasil != null) {
print(hasil.length);
}
}Di atas, blok if mempersempit tipe hasil dari nullable menjadi non-nullable — ini yang disebut type promotion. Gunakan ! hanya saat benar-benar yakin, karena pemaksaan yang salah tetap bisa menyebabkan runtime error.
Akses properti dan method tanpa risiko error memakai ?.:
void main() {
String? nama;
print(nama?.length); // null, bukan error
print(nama?.toUpperCase() ?? 'tidak ada');
}nama?.length mengembalikan null ketika nama null, alih-alih melempar exception. Operator ini menghilangkan rantai pengecekan null yang panjang.
Operator ?? menyediakan nilai fallback, dan ??= meng-assign jika bernilai null:
void main() {
String? konfigurasi;
var nilai = konfigurasi ?? 'default';
konfigurasi ??= 'diset sekali';
print(nilai);
print(konfigurasi);
}konfigurasi ??= 'diset sekali' hanya meng-assign jika konfigurasi masih null. Kombinasi ?., ??, dan ??= membuat kode null-handling yang ekspresif dan aman.
Terkadang field tidak bisa diisi di constructor, misalnya karena bergantung pada data yang baru tersedia nanti. Gunakan late:
class Pengaturan {
late String koneksi;
void muat() {
koneksi = 'redis://localhost:6379';
}
}
void main() {
var p = Pengaturan();
p.muat();
print(p.koneksi);
}late String koneksi menunda inisialisasi hingga muat() dipanggil. Kompiler menjamin bahwa field late selalu diinisialisasi sebelum dibaca.
Dart mempersempit tipe secara otomatis setelah pemeriksaan. Ini bekerja sangat baik untuk variabel lokal dan parameter private:
void proses(Object input) {
if (input is String) {
print(input.toUpperCase());
} else if (input is int) {
print(input + 1);
}
}
void main() {
proses('halo');
proses(41);
}Di dalam blok input is String, kompiler memperlakukan input sebagai String sehingga method toUpperCase tersedia tanpa cast manual. Promotion mengurangi kode boilerplate secara signifikan.
Sound berarti jaminan tipe berlaku di seluruh program, termasuk saat memakai library lama atau interop native. Kompiler dan runtime tidak akan menemukan null di tempat yang secara statis non-nullable. Konsekuensinya: lintasan optimasi lebih agresif dan app lebih kecil serta cepat.
Untuk project yang dibuat sebelum null safety (Dart di bawah 2.12), gunakan tool migrasi resmi:
dart migratedart migrate menganalisis seluruh project dan menyusun perubahan tipe secara interaktif. Setelah selesai, pastikan constraint SDK di pubspec.yaml minimal sdk: ^2.12.0 dan jalankan dart analyze untuk memastikan tidak ada error.
Inti yang harus dibawa pulang:
? untuk membuat nullable.! memaksa nilai non-null hanya jika kalian yakin.?. mengakses tanpa error; ?? memberi fallback; ??= meng-assign jika null.late menunda inisialisasi field; kompiler tetap menjamin keamanannya.is.dart migrate lalu cek dengan dart analyze.Di episode 7 selanjutnya kita akan membahas asynchronous programming — penggunaan Future dengan async dan await, penanganan error, dasar Stream dengan listener dan transformasi, model Isolate untuk concurrency, serta use case asynchronous I/O di aplikasi nyata.