Operator bang

Operator ! sebagai janji kepada analyzer bahwa sebuah nilai tidak null.

Daftar isi

Promosi tipe dan operator null-aware menangani null secara halus, tetapi ada saat ketika kepastian hanya diketahui oleh penulis kode. Operator !, yang lazim disebut operator bang, menyampaikan kepastian itu kepada analyzer dalam bentuk janji bahwa nilai pada ekspresi tersebut tidak null. Sebutan bang berasal dari kebiasaan membaca tanda seru sebagai bang dalam bahasa Inggris. Topik ini membahas cara kerja operator bang, akibat nyata ketika janji itu putus, serta alternatif yang lebih aman sebelum janji semacam itu dibuat.

Janji kepastian kepada analyzer#

Operator ! diletakkan di belakang ekspresi bertipe nullable dan mengubah tipe hasilnya menjadi versi non-nullable. Ekspresi angka! pada variabel bertipe int? menghasilkan int, sehingga operasi aritmetika dan akses anggota dapat dijalankan tanpa pemeriksaan tambahan. Janji ini mengikat dua arah. Analyzer berhenti memperingatkan tentang kemungkinan null, sedangkan penulis kode menjadi bertanggung jawab penuh atas kebenarannya.

Contoh berikut memanfaatkan fungsi int.tryParse yang mengembalikan int?, yaitu hasil penguraian ketika teks valid dan null ketika teks tidak dapat diuraikan.

dart janji.dart
void main() {
  final angka = int.tryParse("42");
  final rusak = int.tryParse("dua puluh");

  print(angka);
  print(rusak);
  print(angka! + 8);
}
bash
# menjalankan program dari folder topik
dart run janji.dart
text Keluaran
42
null
50

Teks "42" berhasil diuraikan menjadi 42, sehingga angka! + 8 menghasilkan 50. Teks "dua puluh" menghasilkan null dan dibiarkan apa adanya karena tidak diberi operator bang. Janji yang dibuat pada baris terakhir adalah bahwa angka pasti berisi angka, dan analyzer menerima janji tersebut tanpa pemeriksaan lebih lanjut.

Operator bang umumnya muncul pada dua keadaan. Pertama, setelah fungsi penguraian seperti int.tryParse yang hasilnya hanya mungkin null untuk masukan tertentu yang sudah diketahui valid. Kedua, pada anggota yang diisi oleh bagian program lain yang urutannya sudah pasti, misalnya bidang yang selalu ditugaskan sebelum metode pembacanya dipanggil. Pada kedua keadaan itu, alasan janji harus tetap dapat dijelaskan karena analyzer tidak lagi memeriksanya.

Tanda ! diletakkan pada ekspresi apa pun yang bertipe nullable, tidak terbatas pada variabel, sehingga bentuk int.tryParse("42")! juga sah. Selain itu, setelah ekspresi angka! dievaluasi dalam alur yang sama, analyzer turut memperlakukan angka sebagai non-nullable pada baris berikutnya, sesuai mekanisme promosi tipe pada topik tipe nullable dan non-nullable. Meskipun demikian, janji semacam itu tetap tidak diverifikasi oleh alat, sehingga baris-baris berikutnya hanya aman selama keadaan yang dijanjikan benar-benar terpenuhi.

Kapan janji itu putus#

Janji operator bang pasti putus ketika nilai di belakangnya ternyata null. Keadaan ini tidak dapat dicegah analyzer karena janji tersebut memang menghentikan pemeriksaannya. Akibatnya muncul pada saat program berjalan, berupa error yang menghentikan eksekusi dan tidak dapat ditunda ke waktu lain.

Berkas berikut menerapkan operator bang pada variabel String? yang belum pernah diberi nilai. Perintahnya menulis berkas tersebut, menjalankannya, lalu menampilkan keluaran yang sebenarnya tercetak.

bash
# membuat berkas yang menerapkan operator bang pada variabel null
mkdir -p /tmp/uji/operator-bang
cat > /tmp/uji/operator-bang/bang.dart <<'EOF'
void main() {
  String? nama;
  print(nama!.length);
}
EOF
# menjalankan berkas untuk melihat error saat berjalan
dart run /tmp/uji/operator-bang/bang.dart
text Keluaran
Unhandled exception:
Null check operator used on a null value
#0      main (file:///tmp/uji/operator-bang/bang.dart:3:13)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:313:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

Pesan Null check operator used on a null value adalah bentuk error khas operator bang yang menemui nilai null. Eksekusi berhenti pada baris itu, dan baris-baris berikutnya tidak pernah dijalankan. Jejak error terbaca dari atas ke bawah, dengan frame bernomor nol sebagai titik kejadian. Frame pertama menunjukkan fungsi main pada baris 3 kolom 13 berkas bang.dart, sedangkan frame berikutnya berisi langkah internal runtime untuk menyiapkan program. Kolom 13 tepat menunjuk posisi tanda !, sehingga ekspresi yang melanggar janji dapat ditemukan tanpa pencarian manual.

Alternatif sebelum menggunakan operator bang#

Tiga alternatif layak dipertimbangkan sebelum menulis !. Pertama, menyusun kode agar promosi tipe bekerja, misalnya dengan pemeriksaan if pada variabel lokal sebagaimana contoh pada pembahasan tipe nullable dan non-nullable. Kedua, menggunakan ?. bersama ?? agar nilai cadangan selalu tersedia. Ketiga, apabila kepastiannya memang tidak dapat dijamin, menangkap errornya secara eksplisit agar program tetap dapat menanggapi keadaan null.

Contoh berikut menangkap akibat janji yang putus dengan blok try, sehingga pesan dan tipe errornya dapat diperiksa tanpa menghentikan program.

dart tangkap.dart
void main() {
  String? nama;
  try {
    print(nama!.length);
  } catch (e) {
    print("pesan: $e");
    print("tipe: ${e.runtimeType}");
  }
}
bash
# menjalankan program dari folder topik
dart run tangkap.dart
text Keluaran
pesan: Null check operator used on a null value
tipe: _TypeError

Error yang tertangkap dikenali sebagai _TypeError dengan pesan yang sama persis dengan versi tidak tertangkap. Mengenal bentuknya berguna untuk membaca jejak error pada program yang sudah berjalan, sedangkan pencegahan yang benar tetap berupa pemeriksaan null atau nilai cadangan. Operator bang paling tepat digunakan ketika kepastiannya berasal dari keadaan yang sudah dipastikan oleh logika program, misalnya variabel yang selalu diisi sebelum dipanggil tetapi tidak dapat dinyatakan sebagai kondisi yang terbaca analyzer.

Urutan pertimbangan tersebut dapat dirumuskan secara sederhana. Pemeriksaan if dengan promosi tipe menjadi pilihan pertama karena tanpa biaya tambahan dan membuat kepastiannya terlihat di kode. Nilai cadangan dengan ?. dan ?? menjadi pilihan kedua ketika ketiadaan nilai memang keadaan yang wajar. Operator bang menjadi pilihan terakhir karena janjinya tidak diverifikasi oleh alat, dan setelah promosi tipe bekerja, tanda ! yang tersisa justru dapat dilaporkan analyzer sebagai penggunaan yang tidak perlu.

Ringkasan#

  • Operator ! mengubah tipe nullable menjadi non-nullable berdasarkan janji penulis kode.
  • Janji yang putus memunculkan error Null check operator used on a null value saat program berjalan.
  • Alternatif yang lebih aman adalah promosi tipe, ?. bersama ??, atau penanganan error eksplisit.
  • Operator bang hanya digunakan ketika kepastiannya sudah dipastikan oleh logika program.

Referensi#