Kelas menyatukan data dan perilaku yang saling berhubungan ke dalam satu tipe, sedangkan konstruktor menyiapkan objek baru sebelum objek tersebut digunakan. Topik ini membahas deklarasi kelas dengan bidang, konstruktor tanpa nama yang menugaskan parameter langsung ke bidang melalui this, konstruktor bernama, initializer list, serta konstruktor konstanta. Setiap contoh berupa berkas utuh yang dijalankan dengan Dart SDK 3.13, sehingga keluaran yang disertakan dapat direproduksi ulang.
Kelas dan bidang#
Kelas (class) adalah cetak biru yang mendefinisikan bidang (field) dan metode (method) yang dimiliki setiap objek (object) yang dihasilkannya. Bidang adalah variabel yang melekat pada objek, sedangkan metode adalah fungsi yang dideklarasikan di dalam kelas dan dapat beroperasi pada bidangnya. Objek dibuat dengan memanggil nama kelas seperti fungsi, misalnya Titik(), tanpa kata kunci new karena kata kunci tersebut bersifat opsional pada Dart modern. Berkas berikut mendeklarasikan kelas Titik dengan dua bidang bertipe int yang masing-masing diberi nilai bawaan 0, lalu fungsi main membuat satu objek, membaca bidangnya melalui notasi titik, mengubah nilainya, dan mencetak hasilnya dua kali.
class Titik {
int x = 0;
int y = 0;
}
void main() {
final titik = Titik();
print('${titik.x}, ${titik.y}');
titik.x = 3;
titik.y = 4;
print('${titik.x}, ${titik.y}');
}0, 0
3, 4Nilai bawaan pada deklarasi bidang diisi saat objek dibentuk, sebelum ada konstruktor yang berjalan. Notasi titik yang sama digunakan untuk membaca dan menulis bidang, sehingga seluruh anggota objek dapat dijangkau dari luar kelas. Pengendalian akses baca dan tulis secara terarah ditangani getter dan setter, yang dibahas pada bagian tersendiri di modul ini.
Konstruktor tanpa nama dan parameter this#
Konstruktor tanpa nama (constructor) adalah anggota kelas yang namanya sama dengan nama kelasnya dan tidak membawa nama tambahan. Bentuk paling ringkas menugaskan parameter langsung ke bidang melalui this, sehingga deklarasi Titik(this.x, this.y) tidak memerlukan badan sama sekali. Bentuk panjangnya setara dengan konstruktor yang menerima dua parameter lalu menugaskannya satu per satu dengan this.x = x dan this.y = y. Tanpa singkatan this, parameter yang bernama sama dengan bidangnya justru menutupi bidang tersebut di dalam badan konstruktor, sehingga penugasan x = x hanya mengisi parameter itu sendiri dan bidangnya tetap kosong. Singkatan this.x mencegah kekeliruan semacam itu karena penugasan ke bidang sudah tercatat pada tanda tangan konstruktor.
class Titik {
int x;
int y;
Titik(this.x, this.y);
}
void main() {
final titik = Titik(3, 4);
print('x: ${titik.x}, y: ${titik.y}');
}x: 3, y: 4Panggilan Titik(3, 4) mengalirkan 3 ke bidang x dan 4 ke bidang y tanpa satu baris penugasan manual pun. Bentuk ini menjamin bidang terisi sejak objek terbentuk, sehingga objek selalu berada dalam keadaan siap digunakan. Konstruktor tanpa nama inilah yang dijalankan apabila kode hanya menyebut nama kelas beserta argumennya.
Konstruktor bernama#
Satu kelas boleh memiliki lebih dari satu konstruktor. Selain konstruktor tanpa nama, Dart menyediakan konstruktor bernama (named constructor) yang ditulis dengan pola NamaKelas.namaKonstruktor. Setiap konstruktor bernama merupakan jalur pembuatan objek yang berdiri sendiri, sehingga satu kelas dapat menawarkan beberapa cara pembuatan yang masing-masing menyatakan maksudnya secara eksplisit.
class Titik {
int x = 0;
int y = 0;
Titik(this.x, this.y);
Titik.asal();
}
void main() {
final pusat = Titik(5, 2);
final asal = Titik.asal();
print('pusat: (${pusat.x}, ${pusat.y})');
print('asal: (${asal.x}, ${asal.y})');
}pusat: (5, 2)
asal: (0, 0)Konstruktor bernama tidak mewarisi isi konstruktor tanpa nama, karena badan dan inisialisasinya ditulis terpisah. Apabila bidang sudah memiliki nilai bawaan pada deklarasinya, konstruktor bernama dengan badan kosong cukup mengandalkan nilai tersebut, sebagaimana Titik.asal() yang menghasilkan titik pada koordinat (0, 0). Konstruktor bernama juga dapat membawa parameternya sendiri, sehingga jalur pembuatan tambahan seperti Titik.denganJarak(jarak) tetap mungkin ditulis. Jumlah konstruktor bernama pada satu kelas tidak dibatasi, sehingga beberapa jalur pembuatan dengan maksud berbeda dapat berdampingan. Penamaan yang jelas juga menghindarkan benturan tanda tangan, karena dua konstruktor tanpa nama pada satu kelas tidak mungkin ditulis bersamaan.
Initializer list#
Initializer list adalah bagian konstruktor yang ditulis setelah titik dua (:) dan berisi ekspresi yang dijalankan sebelum badan konstruktor berjalan. Bagian ini menjadi tempat yang tepat untuk mengisi bidang final serta melakukan pemeriksaan awal dengan assert, karena parameter konstruktor masih dapat diakses pada bagian tersebut dan bidangnya belum berubah sejak objek mulai dibentuk. Ekspresi di dalam initializer list belum boleh mengakses this karena objek belum sepenuhnya terbentuk, sehingga pengisian bidang melalui parameter dan pemeriksaan dengan assert menjadi kegunaan utamanya.
class Rentang {
final int awal;
final int akhir;
Rentang(this.awal, this.akhir)
: assert(awal <= akhir);
}
void main() {
final rentang = Rentang(1, 10);
print('${rentang.awal} sampai ${rentang.akhir}');
}1 sampai 10Bidang awal dan akhir dinyatakan final sehingga keduanya wajib terisi sebelum konstruktor selesai, dan initializer list itulah yang menyediakan jalur pengisiannya melalui singkatan this.awal dan this.akhir. Pernyataan assert menguji syarat sejak awal, sehingga pembuatan Rentang(10, 1) gagal dengan error assertion pada mode pengembangan sebelum objek sempat digunakan. Pengisian melalui initializer list berlaku juga tanpa singkatan parameter, misalnya : awal = 0, akhir = 0 pada sebuah konstruktor bernama, sehingga bidang final selalu memiliki jalur yang sah untuk terisi.
Konstruktor konstanta#
Konstruktor konstanta (const constructor) membuat objek yang nilainya diketahui pada waktu kompilasi. Syaratnya, seluruh bidang kelas bersifat final, konstruktor ditandai const, dan pemanggilannya berada di dalam konteks const. Objek yang dihasilkan dikanonikalisasi (canonicalized), yaitu dua pemanggilan dengan nilai yang sama menghasilkan satu objek yang sama.
class Ukuran {
final int lebar;
final int tinggi;
const Ukuran(this.lebar, this.tinggi);
}
void main() {
const a = Ukuran(10, 24);
const b = Ukuran(10, 24);
print(identical(a, b));
print('${a.lebar}x${a.tinggi}');
}true
10x24Fungsi identical menguji apakah dua variabel menunjuk objek yang sama. Nilai true pada keluaran menunjukkan bahwa a dan b bukan dua objek yang kebetulan berisi nilai sama, melainkan satu objek yang sama. Kanonikalisasi menjadikan konstanta seperti Ukuran(10, 24) aman dibagikan sebagai nilai yang tetap di seluruh program.
Konstruktor konstanta tepat digunakan untuk kelas nilai sederhana yang sering dibuat ulang dengan nilai tetap, misalnya ukuran, koordinat, atau hasil konversi satuan. Pemanggilannya juga sah tanpa konteks const, dan hasilnya tetap berupa objek biasa yang dibentuk saat program berjalan. Perbedaannya hanya muncul pada konteks const, yaitu saat objek dihitung lebih dahulu oleh kompilator lalu digunakan ulang untuk seluruh pemanggilan bernilai sama.
Kesalahan umum#
Bidang final yang tidak pernah diinisialisasi adalah kesalahan yang sering muncul saat menulis konstruktor. Dart menolaknya pada waktu analisis statis karena nilai bidang final tidak boleh berubah setelah terbentuk, dan penugasan di badan konstruktor pun tidak diperbolehkan untuk bidang final. Blok berikut menuliskan berkas bermasalah ke folder sementara, lalu menjalankan dart analyze atas berkas itu.
rm -rf /tmp/uji/konstruktor
mkdir -p /tmp/uji/konstruktor
cat > /tmp/uji/konstruktor/konstruktor_final.dart <<'EOF'
class Sensor {
final String nama;
Sensor();
}
void main() {}
EOF
cd /tmp/uji/konstruktor
export PATH=/home/kal/dart-sdk/bin:$PATH
dart analyze konstruktor_final.dartAnalyzing konstruktor_final.dart...
error - konstruktor_final.dart:4:3 - All final variables must be initialized, but 'nama' isn't. Try adding an initializer for the field. - final_not_initialized_constructor
1 issue found.Analisis di atas terjadi karena konstruktor Sensor() tidak mengisi bidang nama sama sekali. Perbaikan dapat dilakukan dengan salah satu dari tiga jalur, yaitu parameter dengan singkatan seperti Sensor(this.nama), inisialisasi melalui initializer list seperti Sensor() : nama = 'terpasang', atau nilai bawaan pada deklarasi bidang seperti final String nama = 'terpasang'. Pemilihan jalur bergantung pada apakah nilai tersebut selalu diberikan oleh pemanggil ataukah berasal dari kelasnya. Error semacam ini tidak menunggu program berjalan karena pemeriksaannya selesai pada waktu analisis statis. Memberi nilai null bukan jalan keluar, karena bidang final bertipe non-nullable tidak menerima null sejak deklarasinya.
Ringkasan#
- Kelas mendefinisikan bidang dan metode, sedangkan objek dibuat dengan memanggil nama kelasnya, misalnya
Titik(), tanpa kata kuncinew. - Konstruktor tanpa nama dengan parameter
thismenugaskan parameter langsung ke bidang yang bernama sama tanpa badan konstruktor. - Konstruktor bernama seperti
Titik.asal()menyediakan jalur pembuatan tambahan dan ditulis terpisah dari konstruktor tanpa nama. - Initializer list dijalankan sebelum badan konstruktor dan menjadi tempat pengisian bidang final serta pemeriksaan
assert, sedangkan konstruktor konstanta menghasilkan objek waktu kompilasi yang dikanonikalisasi.
Referensi#
- Classes — dart.dev — Dokumentasi resmi kelas Dart, mencakup bidang, konstruktor tanpa nama, konstruktor bernama, dan initializer list.
- Effective Dart — dart.dev — Pedoman desain yang mencakup pemilihan konstruktor dan penulisan anggota kelas.