Dart dilinde Uzantı Metotları(Extension Methods)

Mirkan
Flutter İzmir
Published in
4 min readNov 3, 2019

--

Dart 2.6'nın çıkmasına az bir süre kaldı, ama bu dev kanalına geçip Dart’ın yeni özellikleriyle oynayamayacağımız anlamına gelmiyor.

Bu kısa bir yazı olacak, o yüzden hemen küçük bir örnek ile başlıyorum.

Dart dilinde bir listenin ilk elemanını .first özelliği(property) ile alabiliyoruz fakat String tipinde böyle birşey yok.

// 'hello'[0] ile alabiliriz tabii ama örneğin selameti için biz biraz işleri karıştıralım.

String’ler üzerinde kullanacağınız böyle küçük rutin görevleri yapan metotlarınız varsa muhtemelen projenizde bunları tuttuğunuz böyle bir sınıfınız vardır:

abstract class StringUtils {...
static String first(String str) => str[0];
...
}print(StringUtils.first('hello')); // h

Bunlara static util methods deniyor. Extension metotlar ile ise bunu şu şekilde yapabiliriz:

extension StringExtension on String {
String first() => this[0];
}
print("hello".first()); // h

String sınıfında StringExtension isimli bir uzantı metodu tanımladım, artık ekstra bir sınıfa gerek kalmadan String tipindeki değişkenlerden first() metodu ile ilk karakterini alabiliyorum.

this kelimesi bu metodu çağırdığımız obje, extension metotların this kelimesine erişimi varken, static util metotlarda instance’ı parametre olarak veriyoruz.

Dart’taki List tipini çok kıskandığımızı varsayalım ve first metodumuzu List’deki gibi property’e çevirelim.

extension StringExtension on String {
String get first => this[0];
}
print("hello".first); // h

Property Extension deniyor bunlara, Operator Extension da kullanalım.

extension AnotherStringExtension on String {
String operator >(String other) => "$other$this"
}
print("hello" > "world"); // worldhello

Bu extension ise 2 stringi birleştirmemizi sağlıyor, sırasını değiştirerek.

Kızmayın örnek olması açısından yazdım, bence siz daha faydalı kullanım alanları bulabilirsiniz😄

Generic Type ile Kullanımı

Hem int hem double için bir extension yazmak istediğinizi varsayalım, kendinizi tekrar etmemek için muhtemelen int ve double’ın türediği abstract class olan num classına extension yazmayı düşünebilirsiniz.(Değil mi?😄)

extension NumExtension on num {
num substract(num other) => this - other;
}

Oldu gibi. Şimdi test edelim:

int i = 6;
double d = 4.0;
int newInt = i.substract(2);
int errorInt = d.substract(2);

Kodu çalıştırmadan önce herhangi bir hata yakalamadı editörümüz, fakat çalıştırdığımızda:

Çok temiz patladık. errorInt aslında 2.0. Sonucun tipi double fakat o da num classından türediği için bu hatayı runtime’dan önce(kodu çalıştırmadan önce) yakalayamadık. Alttaki ise kodu çalıştırmadan editörün yakaladığı hata. Çalıştırmadan ne kadar hata yakalarsak o kadar iyi.

Extension’da Generic Type kullanarak biraz kısıtlama getirelim, böylece hatayı daha erken yakalayacağız.

extension AnotherNumExtension<T extends num> on T {
T substract(T other) => this - other;
}

Extension’ı T classında tanımladık ve T classına da sınırlama getirdik, T num’dan türemeli(T extends num). substract metodumuz da T tipinde parametre alıp T tipini döndürecek.

Tekrar bu kısmı toparlamak gerekirse, extension’ı num classında tanımladığımızda ve substract metodu num döndürdüğünde, Dart num tipinin int bir değişkene verilmesinde sakınca görmüyor. Fakat kod çalıştığında bu işlemin sonucu int değil de double çıktığında, int’i double’a eşitleyemem diye uyarıyor. Biz bu hatayı daha erken yakalamak istiyoruz.

T extends num olarak kullandığımızda bir katman daha derine iniyoruz ve Dart artık d - 2.0 de olduğu gibi, this double, other ise int, bunun sonucu double çıkar, yani böyle bir durumda dönüş tipi olan T’ye ben double derim. Bu satırda bunu int’e eşitlemeye çalışmışsın, bu geçersiz diyor.

Extension metotlar ile daha gerçekçi bir örnek

Geçenlerde bir listeden sadece farklı olan elemanları çıkarmak için birşeye ihtiyacım oldu. Mesela [1,1,2,3] listesini [1,2,3] listesine çevireceğim.

abstract class ListUtils {
static List<T> distinct<T>(List<T> paramList) {
List<T> temp = [];
var existing = HashSet<T>();
for (var current in paramList) {
if (existing.add(current)) {
temp.add(current);
}
}
return temp;
}
}
ListUtils.distinct([1,1,2,2,3]); // [1,2,3]

Kısaca açıklarsak, listedeki her bir elemanı HashSet’e ekliyor. HashSet de halihazırda varsa, Set’in add metodu bunu eklemiyor ve Set’i değiştirmediği içinfalse döndürüyor. Set’e ekleyebildiğinde ben de geçiçi bir listeye ekliyorum, döngü bittiğinde bu geçiçi listeyi döndürüyorum.

Extension metotlar ile yukardaki kodu bu hale getirebiliyoruz:

extension ListExtension<T> on List<T> {
List<T> distinct() {
List<T> temp = [];
var existing = HashSet<T>();
for (var current in this) {
if (existing.add(current)) {
temp.add(current);
}
}
return temp;
}
}
[1,1,2,2,3].distinct(); // [1,2,3]

--

--