Sparround

Extension method-lar

Extension — mənbə kodunu dəyişmədən mövcud tipə metod, getter, setter və operator əlavə etməyin yoludur. Dart-ın öz String, int, List tiplərinə də, başqa paketin class-larına da tətbiq oluna bilir.

Elan:

  • extension StringX on String { bool get isBlank => trim().isEmpty; }
  • Adsız da yazmaq olar (extension on String { ... }), amma ad ver — konflikt olanda show/hide və ya açıq çağırış yalnız adla mümkündür.
  • Generic ola bilər: extension ListX<T> on List<T> { T? get firstOrNull => ... }
  • Nullable tip üzərində də mümkündür: extension on String? { ... } — bu halda this null ola bilər.

Extension daxilində this receiver-dir və çox vaxt onu yazmağa ehtiyac qalmır: trim() avtomatik this.trim() deməkdir.

Statik həll — ən sevilən müsahibə tələsi. Extension metodları compile-time-da, dəyişənin statik tipinə görə seçilir. Bu, adi metodlardan prinsipial fərqdir: adi metod runtime-da obyektin real tipinə görə (dynamic dispatch) tapılır.

Nəticələr:

  • Dog dog = Dog(); dog.speak();DogX.speak() işləyir.
  • Animal a = dog; a.speak();eyni obyekt, amma AnimalX.speak() işləyir, çünki statik tip Animal-dır.
  • dynamic d = dog; d.speak();runtime xətası: dynamic üzərində extension ümumiyyətlə axtarılmır.
  • Extension metodu override edilə bilməz — alt class-da eyni adlı extension yazmaq "override" deyil, sadəcə başqa bir extension-dır.

Bir qayda da yadda saxla: tipin öz üzvü həmişə üstündür. String-də length var, sənin extension-dakı length heç vaxt çağırılmayacaq. Extension yalnız tipdə həmin ad yoxdursa işə düşür — buna görə dil komandası String-ə yeni metod əlavə edəndə sənin kodun sınmır, sadəcə davranış dəyişə bilər.

SualExtension methodAdi instansiya metodu
Nə vaxt seçilir?Compile-time, statik tipə görəRuntime, real tipə görə
Override oluna bilər?XeyrBəli
`dynamic` üzərində işləyir?Xeyr — runtime `NoSuchMethodError`Bəli
Private sahəyə çıxışı var?Yalnız eyni kitabxanadadırsaBəli
İmport-dan asılıdır?Bəli — extension scope-da olmalıdırXeyr
Tipin öz üzvü ilə toqquşandaUduzur — tipin üzvü üstündürAid deyil

Konfliktlər. İki import eyni tip üçün eyni adlı extension üzvü gətirirsə, kompilyator qərar verə bilmir və xəta verir. Üç həll yolu var:

  • import 'b.dart' hide Trimmer; — birini gizlət.
  • import 'b.dart' show Squeezer; — yalnız lazım olanı gətir.
  • Açıq tətbiq: Trimmer(' x ').squeeze() — extension adını yazaraq çağır. Bu, import-lardan asılı olmayan, ən dəqiq yoldur və statik tip həllini keçmək üçün də işlədilir: AnimalX(dog).speak().

Extension nə vaxt faydalıdır, nə vaxt zərərlidir?

Faydalıdır: kodun oxunuşunu doğrudan dəyişəndə (19999.azn, '2026-01-01'.toDate(), list.firstOrNull), və ya öz tipinə əlavə etmək istədiyin metodu başqasının paketinə əlavə edə bilmədikdə.

Zərərlidir: gizli xərc gətirəndə (getter-in içində şəbəkə çağırışı və ya ağır hesablama), çox ümumi tiplərə (Object, int) çoxlu ad əlavə edəndə — hər int artıq həmin adı "cavablandırır" və avtokomplit zibillənir, və layihədə eyni ideyanın iki extension-la təkrarlanmasına yol verəndə.

Müsahibə ipucu. Sual demək olar həmişə eyni formada gəlir: "`Animal a = dog; a.speak();` nə çap edəcək?". Doğru cavabın strukturu: (1) "extension-lar compile-time-da, statik tipə görə həll olunur" — bu cümləni birinci de; (2) buna görə AnimalX.speak() işləyir, DogX yox, halbuki obyekt Dog-dur; (3) müqayisə əlavə et: adi metod olsaydı, dynamic dispatch nəticəsində Dog-un versiyası işləyərdi.

Ən çox edilən səhv — extension-ı "class-a metod əlavə etmək" kimi izah etmək. Əslində heç bir şey class-a əlavə olunmur: kompilyator a.speak() çağırışını sadəcə AnimalX.speak(a) şəklində statik funksiya çağırışına çevirir. Bu bir cümlə bütün davranışı izah edir — dynamic üzərində niyə işləmədiyini, niyə override oluna bilmədiyini və niyə import-dan asılı olduğunu.

📚 Mənbələr və sənədlər