Kotlin’de Extension Fonksiyonlar

Barış Can Kurt
Kodluyoruz
Published in
6 min readJun 6, 2021

Kotlin’in bizlere sunduğu güzel feature’larından biri extension fonksiyonlardır. Bir programlama dilinin developer için ne kadar esneklik sağlayabileceğinin güzel örneklerinden biri olan extension functions’ı bu yazımızda inceleyeceğiz.

İnternet üzerinde bu konu hakkında bolca İngilizce içerik mevcut fakat Türkçe içeriklerde extension fonksiyonların yanlış anlaşılmaya açık bir şekilde, sanki inheritance’ın alternatifiymişler gibi anlatıldıklarını gördüm. Bu sebeple bu yazıda elimden geldiğince doğru şekilde açıklamaya çalışacağım. O zaman daha fazla uzatmadan ilk sorumuzla birlikte extension fonksiyonları ele almaya başlayalım. Keyifli okumalar 😊

Extension Function Nedir?

Aslında ismi ne olduğunu gayet iyi şekilde açıklıyor. Kotlin, biz geliştiricilere mevcut classlar üzerine sadece bir fonksiyon tanımı ile yeni fonksiyonellikler kazandırma imkanı sunuyor. Bir örnek üzerinde görmek istersek:

Yukarıda gördüğünüz bir extansion fonksiyon örneğidir. Int class’ı üzerinde basicSquare isimli bir fonksiyon tanımladık. Artık bir Int değişken üzerinde bu fonksiyonu proje içerisinde her yerden (import ettiğimiz sürece) kullanabileceğiz.

Fonksiyonu örnek olması açısından olabildiğince basit tuttum. Fakat belirtmem gerekiyor ki extension fonksiyonlar, class’ın logic fonksiyonlarını içermemelidirler. (Yazının ilerleyen kısmında tekrardan değineceğim.) Görebildiğiniz üzere extension fonksiyon kullanarak Kotlin’in built-in classlarına yeni fonksiyonellikler ekleyebiliyoruz.

Extension Functions Bize Ne Sağlar?

Bu noktada belki de şu soruyu soruyor olabilirsiniz: “Extension functions kavramını anladım ama tam olarak bize ne sağladığını anlayamadım.” Belki de ilk öğrendiğiniz programlama dili Kotlin ve bu özelliğin nasıl artılar kattığını karşılaştırabileceğiniz alternatif bir programlama dili bilginiz yok. Merak etmeyin, sizin için bu soruyu da cevaplayacağız 😊

Fakat bu açıklamanın anlaşılabilmesi için inheritance kavramının bilinmesi gerekiyor. Inheritance’I anlayabilmek için de class ve obje yapısına hakim olmamız gerekli. Maalesef bu yazının 50 sayfa olmasını istemediğimiz için bu kavramlara bakabilmeniz adına sizi şu linke yönlendirmek durumundayım:

Class ve obje mantığını bildiğimizi varsayarak inheritance kavramına değinelim.

Inheritance kelime anlamıyla kalıtsallık demektir. Classları birer şablon (blueprint) olarak düşünebiliriz. Bu şablonları kullanarak objeler üretiriz. Arabayı bir base class olarak düşünürsek X araba modelini o classtan üretilen derived bir class olarak düşünebiliriz. X araba modelinin mağazada gördüğümüz örneği de derived class’ın bir instance’ı olan objedir. Arabamız üzerinde bazı durumlarda değişiklikler yapmamız gerekebilir. Örneğin kar yağdığında kar lastikleri kullanmak isteyebiliriz. Bunun için gidipte kar lastiği olan bir araba satın almayız değil mi? (Makaleyi okuyan kişilerin multi milyarder olmadıklarını varsayıyorum :D )

Normalde yapacağımız şey bir kar lastiği alıp arabamıza takmaktır. İşte extension fonksiyonlar tam olarak bize bu kolaylığı sağlarlar. Extension fonksiyon özelliğinin olmadığı dillerde bir class üzerine yeni bir fonksiyon eklemek istediğimiz zaman yeni bir class oluşturmamız gerekir.

Yeni oluşturduğumuz classın eski classımızın özelliklerini taşımasını istediğimiz için de önceki classtan inherit ederiz. Yani derleyiciye “Ben yeni bir class oluşturmak istiyorum fakat daha önce kullandığım X classının özelliklerini de içermesini istiyorum” deriz. Bunu aşağıdaki Kotlin örneğinde görüldüğü şekliyle yaparız:

Mevcut Araba classına eklenti yapmak istediğimizde yeni bir class oluştururuz:

Yukarıda KarLastikliAraba class’ını iki nokta işaretinden sonra yazdığımız Araba base class’ından türetiyoruz. Bu sayede yeni KarLastikliAraba class’ımızı Araba class’ından inherit ettik.

Inheritence nesne yönelimli programlamada çokça kullanılan önemli bir özelliktir. Fakat ele aldığımız spesifik örnekte olduğu gibi tek bir özellik eklememiz gereken ve bu özelliğin base classın işlevselliğine etkisi olmayan durumlarda pek de ideal değildir. Ya da programlama dilinin kendi sağladığı kütüphaneler içerisindeki classlara (built-in classes) ekleme yapmak istediğimizde (ilk örneğimizde Int classına basicSquare fonksiyonunu extension functions kullanarak kolayca eklemiştik) bir hayli uğraşmamız gerekebilir, class final olarak tanımlanmış olabilir.

Sonuç olarak extension functions bize bu tarz durumlarda yardımcı olur ve çok az bir uğraş ile istediğimiz fonksiyonelliği istediğimiz class’a eklememize izin verir.

Extension Functions Arka Planda Nasıl Çalışırlar?

Aslında extension fonksiyonlar, üzerine yazıldıkları classı genişletmezler. Evet, “amaçsal” olarak extension fonksiyonları mevcut classlarımıza yeni fonksiyonellikler kazandırmak için kullanırız. Fakat arka planda gerçekleşen işlem, istediğimiz class tipleri için nokta notasyonuyla çağırabileceğimiz yeni fonksiyonlar oluşturmaktan ibarettir. Bu yönleriyle “syntactic sugar” olarak düşünülebilirler.

Kotlin’de yazdığımız kod:

Java’ya decompile edilmiş hali:

Özetle üzerlerine tanımlandıkları classların instance’ını alan static fonksiyonlardır diyebiliriz.

Extension Functions Nerede ve Ne Zaman Kullanılmalıdır?

Nullable receiver tiplerle birlikte çağrılabilirler. Örnek olarak:

Yukarıda gördüğümüz şekilde null check yapmadan direk extension fonksiyonumuzu çağırabiliriz.

Operator overloading için kullanılabilirler. Bu konuda daha ileri bilgi için şu linke gidebilirsiniz:

Kodun okunabilirliğini arttırmak için kullanabiliriz.

Infix function oluşturmak için kullanabiliriz. Infix function nedir diyenler için link.

Özet olarak neredeyse tüm durumlarda extension fonksiyonların kullanılmaları geliştiricinin tercihine kalmış durumda.

Extension Functions İsimlendirmeleri ve Klasör Yapısı Nasıl Olmalı?

Kotlinlang dökümanına göre tanımladığımız extension fonksiyonları mümkün olduğunca kendileri için özel olarak oluşturduğumuz bir file içerisinde tutmaktan kaçınmalıyız.

Eğer bir extension fonksiyon belli bir class için tanımlanmışsa o class file’ın içerisine koyulmalıdır. Aslında bunun istenmesindeki mantık şu, bir extension fonksiyonu başka bir dosya içerisinde tanımlanmışsa o dosyanın namespace’ini import etmemiz gerekiyor. Fakat burada aşağıdaki görsellerde görüldüğü gibi kullandığımız IDE bize yardımcı olamıyor:

Bir dosya içerisinde extension fonksiyonumuzu tanımlayalım:

Başka bir dosya içerisinde tanımlanan extension fonksiyona dair bir öneri alamıyoruz:

Fakat tanımladığımız extension fonksiyonu üzerine tanımlandığı classın file’ının içerisinde yazarsak class’I import ettiğimiz zaman beraberinde extension fonksiyonu da import etmiş oluruz. Bu da bize kolaylık sağlar.

İkinci durumda ise eğer yazdığımız extension fonksiyonları üzerine tanımladığımız classın içerisine bir sebepten ötürü koyamıyorsak ya da koymak istemiyorsak “StringExtensions.kt” isimli bir dosya içerisinde tutabiliriz.

Klasör yapısı açısından bakacak olursak da extension fonksiyonlarımızı içeren dosyaları ilgili class dosyalarının olduğu klasörün içerisine koyarak projeye bakan geliştiricinin aralarındaki bağlantıyı kurmasını kolaylaştırabiliriz.

Extension fonksiyonların da normal fonksiyonlar ile aynı yetkiye sahip olduğunu unutmamak gerekiyor. Bu sebeple bir extension fonksiyon ile class içerisindeki gizli kısımlara erişemiyoruz.

Yine Kotlinlang dökümanında extension fonksiyonlarımızın isimlerinin içerisinde extension yazılmaması gerektiği belirtilmiş.

Sonuç Olarak

Bu yazıda extension fonksiyonların sihirli bir şekilde inheritance kullanmadan classları extend etmediklerini, bunun yerine statik bir fonksiyon oluşturarak işimizi kolaylaştırdıklarını öğrendik. Bu açıdan extension fonksiyonları “syntactic sugar” olarak değerlendirmek daha doğru olacaktır. Bununla birlikte amaçlarının mevcut yapılara ufak fonksiyonellikler eklemek olduğunu, birer shortcut olarak kullanılmaları ve classın logic işlemlerini içermemeleri gerektiğini bildiğimiz sürece bu güzel özellikten yararlanabiliriz.

--

--