Kotlin Extensions
C# programlama dilinden tanıdığımız Extension Method’ları, Kotlin de destekliyor. Extension’lar sayesinde (1) yeni bir türetilmiş tip yaratmadan (extend etmeden) (2) mevcut tipi değiştirip tekrar derlemeden, mevcut tiplere yeni özellikler ekleyebiliriz.
Aşağıdaki örnekte java.util.Date sınıfı için yazılmış isSunday() extension’ı var:
Kotlin derleyicisi bir final sınıf yaratıp bu sınıfa java.util.Date tipinde parametre alan, boolean tipinde dönüş değeri olan isSunday adında static bir metod ekler. Java dünyasında, extension metodların karşılığı static metodları olan yardımcı (Util) sınıflardır. Yukarıdaki örneğin Java ile yazılmıs birebir karşılığı aşağıdaki DateUtil sınıfıdır.
Extension metodlarını kullanmak bir best practice midir emin değilim. Faydalarını ve dikkat etmemiz gereken noktaları birer birer tartışalım.
Code Completion
Static tipli programlama dilleri için geliştirilmiş IDE’ler yazılım sürecinde inanılmaz kolaylıklar sağlıyorlar. IntelliJ’de ayarlardan (Editor->General->Code Completion) Code Completion özelliğini kapatıp yarım saat kod yazmayı deneyin ne demek istediğimi anlarsınız.
isSunday örneğinden gidecek olursak, extension metod yerine yardımcı sınıf ve static metod kullandığımızı düşünelim. isSunday() metodunu kullanabilmek için önce bu metodun hangi sınıfta tanımlanmış olduğunu bulmamız gerekir. DateUtil, DateUtils, DateHelper, Dates aklıma gelen birkaç sınıf ismi. Ancak doğru sınıfı bulduktan sonra IDE’nin Code Completion özelliğini kullanabiliriz.
Extension metodu kullanırsak IDE bize aşağıdaki resimdeki gibi bir kıyak yapabilir. Çünkü isSunday metodu rastgele bir yardımcı sınıfın static metodu değil Date sınıfının bir extension metodudur.
Static dispatch
java.sql.Date, java.util.Date sınıfından türetilmiş bir sınıftır. Sizce aşağıdaki kod bloğu çalıştırılırsa ekrana ne yazar?
date1 ve date2‘nin runtime tipi aynı (java.sql.Date) olmasına rağmen yukarıdaki kodun çıktısı şöyledir:
java.util.Date 14:55
java.sql.Date 14:55
Bunun sebebi extension metodların dispatch edilmesinin dinamik değil statik olmasıdır. Bir başka deyişle derleyici, hangi extension metodu çalıştıracağına date1 ve date2‘nin derleme sırasında tanımlı olan tiplerine bakarak karar verir.
‘Member’ fonksiyonlar her zaman kazanır
Mevcut sınıf metodu ile aynı isme, aynı sayıda ve tipte parametreye sahip bir extension metodu yazarsanız sizin metodunuz değil ‘member’ metod çağırılır.
3rd party bir sınıf için extension metodu kullandığımızı düşünelim. Bu sınıfa ilerleyen versiyonlarda extension metodumuz ile aynı imzaya sahip bir metod eklenirse artık bizim metodunuz değil sınıfın gerçek metodu çağırılmaya başlanır ve bundan haberdar olmamız neredeyse mümkün değildir.
Extension metod’ları nasıl organize etmek lazım?
Extension metod’ları ayrı bir dosyaya ya da kullanmak istediğiniz bir sınıf dosyasının içine yazabilirsiniz. Düzenli olması için benim tercihim
- net.peakgames.extensions.libgdx
- net.peakgames.extensions.json
- net.peakgames.extensions.collections
gibi kendi package’ları altında (namespace) tanımlamak.
Yukaridaki örnekte LibGDX Actor’leri için basit bir extension metod tanımı var. Bu extension’ı net.peakgames.extensions.libgdx paketi altına LibGDXExtensions dosyasına yazıyoruz ve kullanmadan önce aşağıdaki gibi import etmemiz gerekiyor:
LibGDXExtensions dosyasının başına yazdığımız @file:JvmName annotation, Kotlin derleyicisinin LibGDX extention’ları için oluşturacağı sınıfın ismini belirtmek için kullanılıyor. @file:JvmName belirtmeseydik Kotlin LibgdxExtensionsKt adında bir sınıf oluşturacaktı. Yukarıda yazdığımız opacity metodunu Java’dan aşağıdaki gibi çağırabiliriz.
Sonuç olarak dikkatli ve abartmadan kullanıldığında Extension metodlar, kodun daha okunaklı olmasına yardımcı oluyorlar. Şunu hiçbir zaman aklımızdan çıkartmamamız lazım:
With Great Power Comes Great Responsibility