Swift Generics

Bu yazımda şu konuları anlatmaya çalışacağım:

  • Generic nedir?
  • Genericlere neden ihtiyaç var?
  • Generic bir fonksiyon tanımı nasıl yapılır?
  • Genericler sadece fonksiyonlarla mı tanımlanabilir?
  • Swift Generic Type Constraints
  • Generic Protocol Kavramı
  • Generic protocol nasıl oluştururuz?

Generic nedir?

  • Türden bağımsız işlem yapabilmeyi sağlayan mekanizmadır.
  • C++ dilinde template olarak geçmektedir. Java ve C# dillerinde generic olarak eklenmiştir.
  • Swift dilinde ki generic yapılar daha fazla C++ dilinde ki template mekanizma işleyişine benzemektedir.

Genericlere neden ihtiyaç duyuyoruz?

  • Bazı senaryolarda farklı türler için aynı işi yapan birden fazla fonksiyon yada metodun yazılması gerekebilir. Tüm bu fonksiyonları tek tek yazmamak için biz generic mekanizmasını kullanıp bir tane şablon yazarız ve derleyici bizim yerimize türden bağımsız, diğer türler için gerekli işlemleri yapar.

Senaryo: Parametre olarak liste alan bir fonksiyon geriye listenin en büyük elemanını döndürsün.

  • Yazdığımız fonksiyon Int dizisi için çalışıyor ama bizim senaryomuzda böyle bir kısıtlama yok. Yani Double dizide gönderseler sonucu doğru şekilde görmeliyiz. Swift dilinde bunu yapabilmek için Double içinde ayrı bir fonksiyon yazmalıyız. İşte burada her tür için fonksiyon yazmak yerine GENERIC konusu gelip bizi kurtarıyor. Ve bu kısmı SWIFT COMPILER bizim için hallediyor.

Generic bir fonksiyon tanımı nasıl yapılır?

  • Swift dilinde generic fonksiyon tanımı, fonksiyon isminden sonra <> gibi açısal parantezler arasında tür parametrelerinin bildirilmesiyle yapılır. Örneğin

Yukarıda ki kodda gördüğümüz T, K, M herhangi bir tür parametresi. Buraya istediğiniz harflendirmeyi yapabilirsiniz. Genel olarak bu isimlendirmelerde tek harf kullanılır ama bu bir kural değil tercihtir.

Senaryo: Bir swap fonksiyonunu generic kullanarak yazalım.

Sonuç: Swap fonksiyonunu tanımlarken T herhangi bir tür oluyor. Burada derleyici argümanlara bakarak türü otomatik tespit ediyor. Örneğin fonksiyon çağırılırken parametreler string olduğu için derleyici T türünün String olduğunu anlıyor.

  • Swift dilinde C++ dilinde olduğu gibi generic türlerin açıkça belirtilmesi özelliği yoktur. Bu nedenle swift dilinde tüm generic tür parametrelerinin fonksiyon imzası içerisinde kullanılması zorunludur. Söylemek istediğimi kod üzerinde göstereyim.

Genericler sadece fonksiyonlarla mı tanımlanabilir?

  • Tabi ki hayır. Generic class, struct, enum tanımlanabilir. Önemli nokta swift dilinde prokoller GENERİC OLAMAZ.
  • Generic sınıf, yapı, enum tanımlamak, fonksiyon tanımlamakla aynı yapıyı kullanıyor. Örneğin
class AlgoList<T>
{
// KODLAR
}

Senaryo: Bir stack veri yapısını generic mekanizması kullanarak yazalım.


Swift Generic Type Constraints

  • Bir generic tanımında tür parametreleri hangi türden açılırsa açılsın anlamla olmak zorundadır. Aksi durumda compile time’da hata oluşur. Ne demek istediğimizi hemen bir örnek üzerinden açıklayım.

Evet burada bir problem var. Şöyle bir hata alıyoruz.

// Binary operator ‘==’ cannot be applied to two ‘T’ operands

Burada her T türünün == operator fonksiyonu olmak zorunda değil. İşte bu tür durumlarda generic parametrelerine bazı kısıtları sağlamak zorunluluğu getirebiliriz.

Swift dilinde type constraints nasıl yapıyoruz?

<turParametresi> : <ProtocolListesi>
  • Bu şekilde gene anlaşılmamış olabilir. Yukarıda yazdığımız fonksiyon parçamıza ekleyelim.

Peki burada neler oldu?

  • Burada derleyici T türünün Equatable protokolünü destekleyen bir türle açılacağı GARANTİSİ verildi. Artık fonksiyon Equatable protokolünü desteklemeyen bir türle çağırmaya çalışırsak derleme aşamasında error oluşur.
  • Equatable protokolü ==, != destekliyor. Bunların dışında başka bir operatör kullanalım. Koda göre çok mantıklı olmasa da sadece hatayı görmek için <= kullanacağım. Alacağımız derleyici hatasını görmek için.
  • Şimdi yukarıda verdiğimiz formüle, yazım kuralanı göre <turParametresi>: <ProtocolListesi> vardı. Tabi burada protocol konusunuda bilmemiz gerekiyor. Equatable prokolu sadece == ve != destekliyordu.

Yukarıda ki hatayı kaldırabilmek için ne yapabiliriz?

  • Comparable protokolü Equatable protokolünden türetilmiştir. Bu protokolde yalnızca < operatör fonksiyonu vardır.
  • Yani Comparable protokolünü destekleyen bir tür hem == hem de < operatör fonksiyonlarını bulundurmak zorundadır.
  • Bu iki operatör fonksiyonu bulunduğunda kütüphanedeki !=, <=, >= ve > generic operatör metotları devreye girerek != <=, >= ve > işlemlerini == ve < operatörlerini kullanarak yapmaktadır.
  • Dolayısıyla bir tür Comparable protokolünü destekliyorsa biz o turu 6 karşılaştırma operatörüyle de işleme sokabiliriz. Kafamız karışmış olabilir. Hemen yukarıda ilk tanımladığımız getMax fonksiyonu üzerinden örnek verelim.

Swift dilinde generic protocol kavramı var mı?

  • Swift dilinde generic protocol kavramı yok. Ama dolaylı yollardan oluşturabiliyoruz.

Generic protocol nasıl oluştururuz?

  • Swift dilinde associatedtype adında anahtar sözcük bulunuyor. Bu anahtar sözcük ile bir tür ismi oluşturursak protokolün elemanlarını buna dayalı olarak oluşturabiliriz. Böyle anlaması gene zor oluyor. Örnek üzerinden gidelim.

Yukarıda yazdığımız protocol bir tür destekleyecek ise T türü ne olursa olsun, protocol SADECE BİR TANE uygun method bulundurmak zorundadır.