Operator menghubungkan nilai menjadi ekspresi, dan Dart mendefinisikan perilaku setiap operator secara ketat agar hasil ekspresi dapat diprediksi. Topik ini membahas operator aritmetika, kesetaraan dengan == dan fungsi identical(), pengujian tipe dengan is, is!, dan as, serta cascade (cascade) untuk merantai panggilan pada satu objek. Setelah membacanya, ekspresi aritmetika dapat ditulis dengan mengetahui tipe hasilnya, dan rantai panggilan yang panjang dapat dirapikan.
Operator aritmetika#
Operator aritmetika mencakup penjumlahan +, pengurangan -, perkalian *, pembagian /, pembagian bulat ~/, dan sisa pembagian %. Empat yang pertama bekerja sesuai harapan, sedangkan dua yang terakhir perlu penjelasan singkat. Operator ~/ membagi lalu membuang bagian pecahannya, sehingga 7 ~/ 2 menghasilkan 3. Operator % mengembalikan sisa pembagian, sehingga 7 % 2 menghasilkan 1. Dart juga menyediakan penurunan tanda - untuk satu operan serta ++ dan -- untuk menambah atau mengurangi satu.
Urutan pengerjaan (precedence) mengikuti konvensi matematika, sehingga perkalian dan pembagian dikerjakan lebih dahulu daripada penjumlahan dan pengurangan dalam satu ekspresi. Apabila urutan bawaan tidak sesuai maksud, tanda kurung digunakan untuk menegaskan urutan yang diinginkan. Kebiasaan menaruh tanda kurung pada ekspresi campuran antara bilangan bulat dan pecahan membuat hasil hitungan tetap mudah dibaca oleh pembaca kode berikutnya.
Contoh berikut memanggil keenam operator tersebut dalam satu program.
void main() {
var totalBarang = 7 + 5;
var selisihStok = 20 - 3;
var hasilKali = 6 * 4;
var hasilBagi = 7 / 2;
var pembagianBulat = 7 ~/ 2;
var sisaBagi = 7 % 2;
var penurunan = -totalBarang;
print('Total barang: $totalBarang');
print('Selisih stok: $selisihStok');
print('Hasil kali: $hasilKali');
print('7 / 2 = $hasilBagi');
print('7 ~/ 2 = $pembagianBulat');
print('7 % 2 = $sisaBagi');
print('Penurunan: $penurunan');
}Total barang: 12
Selisih stok: 17
Hasil kali: 24
7 / 2 = 3.5
7 ~/ 2 = 3
7 % 2 = 1
Penurunan: -12Keluaran menegaskan dua perilaku yang penting diingat. Pembagian 7 / 2 menghasilkan 3.5 karena operator / selalu menghasilkan double, sedangkan 7 ~/ 2 menghasilkan 3 karena bagian pecahan dibuang. Pemilihan operator yang tepat menghindari kebingungan tipe, terutama saat hasil pembagian digunakan untuk menghitung indeks atau jumlah halaman.
Kesetaraan dengan == dan identical#
Operator == membandingkan kesetaraan dua nilai, sedangkan fungsi identical() memeriksa apakah dua variabel menunjuk objek yang sama persis di memori. Untuk kebanyakan objek, == berperilaku sama dengan identical(), kecuali pada tipe yang mendefinisikan ulang ==, seperti String yang membandingkan isi dan int yang membandingkan nilai. Dua daftar dengan isi sama tetap tidak dianggap sama oleh ==, karena List tidak mendefinisikan ulang ==. Contoh berikut membuat dua daftar berisi anggota identik beserta satu variabel yang menunjuk daftar yang sama.
void main() {
var daftarA = [1, 2, 3];
var daftarB = [1, 2, 3];
var daftarC = daftarA;
print(daftarA == daftarB);
print(daftarA == daftarC);
print(identical(daftarA, daftarB));
print(identical(daftarA, daftarC));
}false
true
false
truePerbandingan daftarA == daftarB menghasilkan false karena keduanya objek berbeda yang kebetulan berisi anggota sama. Sebaliknya, daftarA == daftarC menghasilkan true karena daftarC menunjuk objek yang sama dengan daftarA. Fungsi identical() menghasilkan nilai yang persis sama pada kasus ini, dan bedanya baru terlihat pada tipe yang mendefinisikan ulang ==. Pada tipe semacam itu, dua String berisi sama dianggap setara oleh ==, sedangkan identical() baru menghasilkan true apabila kedua variabel benar-benar menunjuk objek string yang sama.
Pengujian tipe dengan is, is!, dan as#
Operator is menguji apakah nilai bertipe tertentu dan menghasilkan bool, sedangkan is! menguji kebalikannya. Operator ini berguna ketika variabel bertipe luas seperti num perlu diketahui bentuk aktualnya. Setelah pengujian is berhasil, analyzer memperlakukan variabel sebagai tipe yang diuji di dalam blok yang sama, sehingga pemanggilan anggota khusus tipe tersebut langsung sah. Operator as berbeda fungsi, yaitu memaksa tipe secara eksplisit (cast). Apabila nilai ternyata tidak cocok dengan tipe sasaran, kegagalannya berupa exception, yaitu gangguan yang menghentikan alur program.
Contoh berikut menguji variabel bertipe num, menolak tipe String, lalu memaksa num menjadi int.
void main() {
num nilai = 87;
print(nilai is int);
print(nilai is! String);
var angka = nilai as int;
print(angka + 1);
}true
true
88Ekspresi nilai is int menghasilkan true karena 87 disimpan sebagai bilangan bulat, dan nilai is! String menghasilkan true karena nilainya bukan string. Pemanggilan nilai as int menugaskan nilai yang sama ke variabel bertipe int tanpa mengubah nilainya. Apabila cast dilakukan ke tipe yang tidak cocok dengan nilai aktualnya, misalnya nilai as String, program berhenti dengan exception TypeError bertuliskan type 'int' is not a subtype of type 'String' in type cast. Untuk kasus seperti itu, pengujian dengan is lebih aman karena kegagalannya berupa nilai false, bukan exception.
Cascade untuk rantai panggilan#
Cascade (cascade) ditulis .. dan memungkinkan beberapa panggilan dilakukan pada objek yang sama tanpa mengulang nama variabelnya. Ekspresi pertama menyiapkan objek, lalu setiap .. meneruskan objek itu sebagai sasaran panggilan berikutnya, dan nilai keseluruhan ekspresinya adalah objek tersebut sendiri. Pola ini menghemat penulisan ketika beberapa metode atau bidang perlu diatur sekaligus pada objek baru. Kelas sederhana pada contoh berikut hanya berperan sebagai wadah, dan mekanismenya dibahas lengkap pada modul Objek dan kelas.
// Rekapitulasi nilai ujian memakai cascade untuk merantai panggilan metode.
class Penilaian {
double totalNilai = 0;
int jumlahNilai = 0;
void tambah(double nilai) {
totalNilai += nilai;
jumlahNilai += 1;
}
void reset() {
totalNilai = 0;
jumlahNilai = 0;
}
}
void main() {
var penilaian = Penilaian()
..tambah(80)
..tambah(90)
..tambah(70);
print('Rata-rata: ${penilaian.totalNilai / penilaian.jumlahNilai}');
penilaian
..reset()
..tambah(100);
print('Rata-rata setelah reset: ${penilaian.totalNilai / penilaian.jumlahNilai}');
}Rata-rata: 80.0
Rata-rata setelah reset: 100.0Tanpa cascade, tiga panggilan tambah() harus ditulis sebagai tiga pernyataan dengan menyebut penilaian berulang kali, sedangkan cascade merapatkan semuanya menjadi satu ekspresi. Rantai kedua memperlihatkan sifat lain yang berguna, yaitu cascade dapat memadukan metode apa pun pada objek yang sama, sehingga reset() diikuti tambah(100) tetap berlangsung pada objek penilaian yang sama. Pembagian untuk menghitung rata-rata tetap dilakukan setelah rantai selesai, karena nilai akhir ekspresi cascade adalah objek itu sendiri.
Untuk objek yang mungkin bernilai null, Dart menyediakan cascade null-aware yang ditulis ?.., yaitu bentuk yang melewati seluruh rantai apabila objeknya null. Rincian null dan tipe nullable dibahas pada modul Sound null safety, sehingga di topik ini cascade .. pada objek yang pasti terisi sudah mencakup kebutuhan sehari-hari.
Ringkasan#
- Operator
/selalu menghasilkandouble,~/menghasilkan pembagian bulat, dan%menghasilkan sisa pembagian. ==membandingkan kesetaraan nilai sesuai definisi tipenya, sedangkanidentical()memeriksa kesamaan objek di memori.isdanis!menguji tipe dengan hasilbool, sedangkanasmelakukan cast yang kegagalannya berupa exception.- Cascade
..merantai beberapa panggilan pada satu objek dan bernilai objek itu sendiri, sedangkan?..menangani objek yang mungkin null.
Referensi#
- Operators — dart.dev — Dokumentasi resmi operator Dart, termasuk tabel aritmetika, kesetaraan, pengujian tipe, dan cascade.
- Language — dart.dev — Peta fitur bahasa Dart untuk menelusuri topik operator lebih jauh.
- API reference — api.dart.dev
— Referensi API yang memuat fungsi
identical()beserta kontrak perilakunya.