Extension menambahkan anggota baru pada tipe yang tidak dapat diubah, misalnya String dan int milik SDK. Topik ini membahas deklarasi extension dengan on, properti terhitung beserta penggunaan this di dalamnya, dan resolusi statis yang menentukan kapan anggota extension tersedia. Error pada penerima bertipe salah serta perilaku pada referensi bertipe dynamic ditampilkan beserta keluaran analyzer dan runtime yang asli.
Menambahkan metode pada tipe bawaan#
Deklarasi extension Nama on Tipe mendefinisikan anggota tambahan yang dapat dipanggil pada setiap nilai bertipe tersebut melalui notasi titik biasa. Anggota extension tidak menyentuh definisi tipe aslinya, sehingga tipe bawaan SDK maupun tipe dari package pihak ketiga dapat diperkaya tanpa mengubah kode sumbernya. Extension sebaiknya diberi nama agar dapat dipanggil secara eksplisit dan mudah diurai apabila terjadi benturan nama antar-extension.
extension PenghitungKata on String {
int hitungKata() {
final teksRapi = trim();
if (teksRapi.isEmpty) {
return 0;
}
return teksRapi.split(RegExp(r'\s+')).length;
}
}
void main() {
final kalimat = ' penghitung kata sederhana ';
print(kalimat.hitungKata());
print(''.hitungKata());
}3
0Di dalam badan extension, metode hitungKata() boleh memanggil anggota tipe penerimanya secara langsung, termasuk trim() milik String. Pemanggilan kalimat.hitungKata() tampak seperti metode bawaan, padahal yang berjalan adalah anggota extension yang dilekatkan pada tipe String. Logika yang sama berlaku untuk kebutuhan pembersihan teks lain, misalnya menghapus tanda baca atau menyusun singkatan dari beberapa kata.
Extension boleh bersifat generik, misalnya extension Pertama<T> on List<T> untuk menambahkan anggota pada daftar dengan elemen apa pun. Extension tanpa nama juga sah, tetapi hanya dapat digunakan secara implisit karena tidak ada nama yang bisa dipanggil secara eksplisit. Selain itu, anggota extension hanya terlihat pada berkas yang mengimpor pustaka tempat extension tersebut dideklarasikan, sehingga penjangkauannya mengikuti aturan impor yang sama dengan anggota kelas biasa.
Properti terhitung dan this#
Extension dapat memuat metode, getter, setter, dan anggota statis, tidak terbatas pada metode biasa. Getter pada extension menjadi properti terhitung yang diakses tanpa tanda kurung, dan kata kunci this di dalam badan extension merujuk pada nilai penerima tempat anggota dipanggil. Dua bentuk anggota ini membuat extension mampu menyediakan perilaku sekaligus nilai turunan bagi tipe yang sudah ada.
extension Kuadrat on int {
int get kuadrat => this * this;
}
void main() {
const angka = 7;
print(angka.kuadrat);
print(12.kuadrat);
}49
144Getter kuadrat menghitung this * this, sehingga setiap int memperoleh properti baru tanpa mengubah definisi tipe int itu sendiri. Baris 12.kuadrat menunjukkan bahwa anggota extension juga dapat dipanggil langsung pada literal. Apabila tipe penerimanya sudah memiliki anggota bernama sama, anggota asli selalu menang karena prioritasnya lebih tinggi daripada anggota extension.
Setter juga dapat ditulis di dalam extension dengan bentuk set namaNilai(nilai), sehingga tipe penerimanya memperoleh properti yang dapat ditugaskan nilai. Anggota statis extension menjadi tempat utilitas yang berhubungan dengan tipe tersebut tanpa membutuhkan nilai penerima. Dengan pilihan anggota yang lengkap, extension dapat menutupi kebutuhan penyesuaian tipe pihak ketiga tanpa menyentuh kode sumber tipe aslinya.
Resolusi statis: tipe deklarasi menentukan#
Resolusi anggota extension terjadi pada waktu kompilasi berdasarkan tipe statis (static type) penerima, bukan berdasarkan tipe objek yang terbentuk saat program berjalan. Akibatnya, anggota extension hanya tersedia apabila variabel dideklarasikan dengan tipe yang dicakup klausa on. Blok berikut menuliskan berkas bermasalah ke folder sementara, lalu menjalankan dart analyze atas berkas itu.
rm -rf /tmp/uji/extension-analisis
mkdir -p /tmp/uji/extension-analisis
cat > /tmp/uji/extension-analisis/extension_tipe_salah.dart <<'EOF'
extension PenghitungKata on String {
int hitungKata() {
final teksRapi = trim();
if (teksRapi.isEmpty) {
return 0;
}
return teksRapi.split(RegExp(r'\s+')).length;
}
}
class Dokumen {}
void main() {
final dokumen = Dokumen();
print(dokumen.hitungKata());
}
EOF
cd /tmp/uji/extension-analisis
export PATH=/home/kal/dart-sdk/bin:$PATH
dart analyze extension_tipe_salah.dartAnalyzing extension_tipe_salah.dart...
error - extension_tipe_salah.dart:15:17 - The method 'hitungKata' isn't defined for the type 'Dokumen'. Try correcting the name to the name of an existing method, or defining a method named 'hitungKata'. - undefined_method
1 issue found.Analisis menolak pemanggilan tersebut karena tipe statis Dokumen tidak dicakup klausa on String, meskipun nama metodenya persis sama. Keberlakuan extension tidak meluas ke tipe lain, sehingga satu extension hanya melayani tipe yang disebut pada deklarasi on-nya. Kelas yang membutuhkan hitungKata() harus menyalin logika itu, menambahkan extension sendiri, atau mengubah tipe penerimanya. Aturan yang sama berlaku apabila tipe penerimanya berasal dari package pihak ketiga, sehingga extension yang ditulis untuk package lain tetap mengikuti pemeriksaan tipe statis yang sama.
Benturan nama antar-extension diuraikan dengan cara yang serupa, yaitu panggilan eksplisit seperti PenghitungKata(teks).hitungKata() menetapkan extension yang dipilih. Panggilan eksplisit tersebut mengikat extension pada nilai yang disebut, sehingga resolusinya tetap berpijak pada tipe statis dan nama extension. Nama extension harus ditulis utuh pada bentuk ini, sehingga penulisan semacam itu umumnya disimpan untuk mengurai benturan dan bukan untuk pemanggilan sehari-hari.
Kasus berikutnya lebih tersembunyi karena analyzer tidak menampilkan peringatan apa pun. Blok berikut menjalankan berkas dengan referensi bertipe dynamic menggunakan dart run. Berkasnya ditulis ke folder sementara yang berbeda agar hasil kedua uji tidak saling menimpa.
rm -rf /tmp/uji/extension-dinamis
mkdir -p /tmp/uji/extension-dinamis
cat > /tmp/uji/extension-dinamis/extension_dinamis.dart <<'EOF'
extension PenghitungKata on String {
int hitungKata() => trim().split(RegExp(r'\s+')).length;
}
void main() {
dynamic teks = 'halaman utama';
print(teks.hitungKata());
}
EOF
cd /tmp/uji/extension-dinamis
export PATH=/home/kal/dart-sdk/bin:$PATH
dart run extension_dinamis.dartUnhandled exception:
NoSuchMethodError: Class 'String' has no instance method 'hitungKata'.
Receiver: "halaman utama"
Tried calling: hitungKata()
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
#1 main (file:///tmp/uji/extension-dinamis/extension_dinamis.dart:7:14)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:313:19)
#3 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)Referensi bertipe dynamic melewati pemeriksaan statis, sehingga analyzer menganggap berkas tersebut bersih dan program baru gagal pada saat berjalan. Pesan NoSuchMethodError menunjukkan bahwa resolusi dinamis tidak mengenal anggota extension, karena extension hanya diurai dari tipe deklarasi. Untuk menjangkau anggota extension dari nilai bertipe dynamic, tipe penerimanya ditetapkan terlebih dahulu, misalnya dengan penugasan final teks = nilai as String; sebelum pemanggilan.
Ringkasan#
- Extension ditulis dengan pola
extension Nama on Tipedan menambahkan metode, getter, setter, serta anggota statis pada tipe yang tidak dapat diubah. thisdi dalam badan extension merujuk nilai penerima, sehingga getter sepertikuadratdapat menghitung nilai turunan dari setiapint.- Resolusi terjadi secara statis berdasarkan tipe deklarasi penerima, sehingga anggota extension tidak terjangkau melalui referensi bertipe
dynamicdan gagal denganNoSuchMethodErrorsaat program berjalan. - Benturan nama antar-extension ditolak analyzer dan diurai dengan pemanggilan eksplisit
NamaExtension(nilai).anggota.
Referensi#
- Extension methods — dart.dev — Dokumentasi resmi deklarasi extension dan aturan resolusi statisnya.
- Language — dart.dev — Peta fitur bahasa Dart yang menempatkan extension di antara anggota kelas dan tipe bawaan.