Kotlin’de Yüksek Seviye Fonksiyonlar (Higher-Order Functions)

Image designed by Author on Canva
2010’da Oracle, Sun Microsystems’ı satın aldığından beri Oracle ile Google arasında bir Java savaşı sürmekteydi. Oracle, Java’nın ücretsiz olmayan özelliklerini ücret ödemeden kullanmakla suçladığı Google’a büyükçe bir dava açtı falan filan… Hikâyeyi biliyorsunuzdur, sanırım hepsini anlatmama gerek yok. GoogleIO 2017 etkinliğinde Kotlin, Google tarafından resmî programlama dili ilan edildi. O gün bugündür, Android geliştiricileri olarak Kotlin öğreniyoruz.
“Kotlin, fonksiyonel dil özelliklerine sahiptir,” cümlesini -ben dâhil- pek çok Kotlin ve Android hayranından duymuş olabilirsiniz. “Nedir yani bu fonksiyonel dil özellikleri? Haydi örnek ver, haydi!” diyorsanız toplanın yamacıma. Bu yazıda, Kotlin’in fonksiyonel dil özelliklerinden birisi olan yüksek seviye fonksiyonların kullanımını anlatacağım.
Fonksiyonel dillerde fonksiyonlar özel ve tektir. Literatürde bu, first class tabiri ile ifade edilir. Yani fonksiyonlar; String, Integer veya herhangi bir sınıf ile aynı seviyededir. Fonksiyonlara parametre olarak String veya Integer yollayabildiğimiz gibi, first class fonksiyonlara sahip dillere de parametre olarak fonksiyon yollayabiliriz. Yani, fonksiyona parametre olarak fonksiyon yolluyoruz. Aynı şekilde, başka fonksiyonlardan return tipi olarak fonksiyon da döndürebiliriz.
Kotlin gibi fonksiyonel dillerde fonksiyonlar Java’nın aksine herhangi bir sınıf içerisinde yer almak zorunda değillerdir. Fonksiyon çağıran fonksiyonlara yüksek seviye fonksiyonlar denir ve fonksiyonel diller, yüksek seviye fonksiyon olma özelliğine sahiptirler. Kotlin de doğal olarak bu özelliğe sahiptir. Ayrıca fonksiyonel dillerde fonksiyonlar veri yapılarında ve değişkenlerde tutulabilirler.
Özet olarak Higher-Order Functions, Kotlin’in gözde özelliklerinden birisidir. Şimdi bunu önce küçük bir örnek ile daha iyi açıklayacağım, ardından ise Android’deki kullanımını göstererek açıklamamı pekiştireceğim. İzninizle, başlayalım:
Parametre olarak fonksiyon kabul eden bir fonksiyonun tanımlanması aşağıdaki gibi yapılır:
fun passMeFunction (text: String, function: ( ) -> Unit) {
// do something
// execute the function
function()
}passMeFunction ( “text” ) { print ( it ) }
passMeFunction ( “text” ) { s -> print ( s ) }
passMeFunction ( “text” , { print ( it ) } )
passMeFunction ( “text” , { s -> print ( s ) } )
passMeFunction (“text”, ::print ) // KISS : Keep It Small Stupid hell yeeaaBen açıkçası bu özelliğe ihtiyaç duyduğumun farkında bile değilmişim. Kullanmaya başladığımda anladım ki pek çok yerde higher-order function kullanabilirim. Özellikle orta ve daha büyük ölçekte bir proje yazarken pek çok defa aynı koda başka Activitylerde, Fragmentlarda, sınıflarda ihtiyaç duyarsınız. Mesela, kullanıcı silme işlemi kullanıcı listesinden de kullanıcı detay sayfasından da gerçekleştirilebilir ancak kopyala-yapıştır yapmaya başlarsanız büyük projede ilerleyen safhalarda boğulabilir, çöp kod üretebilir ve kod yönetimini kendi elinizle zorlaştırabilirsiniz.

Screenshot by author
Kullanım Örneği:
Gerçek yaşam örneğine gelirsek: Peakup’ta; çalışanların hızlıca izin alabilmelerini, izin sürecini takip edebilmelerini, takım liderlerinin ve insan kaynaklarının da bu süreci kolayca yönetebilmelerini sağlayan bir Android uygulamamız var: Leave Management. Bu uygulamamızı yazarken bir sorunla karşılaştım: İzin silme, onaylama ve reddetme işlemleri için hemen hemen aynı fonksiyonu yazdığımı ve gereksiz kod tekrarı yaptığımı fark ettim. 3 operasyonda da izin detay DialogFragment’ını önce kapatıp sonra “Silmek / reddetmek / onaylamak istediğinize emin misiniz?” diye soran bir dialog çıkartıyor, kullanıcı onay butonuna dokununca da ilgili fonksiyon aracılığı ile backend’e request yolluyordum. Yazılan fazladan kodu fark ettiğimde aklıma Higher Order Function özelliği geldi çünkü 3 fonksiyonda da sadece Dialog’da yazan text ve çağrılan request fonksiyonu farklıydı. Şöyle bir fonksiyon yazdım:
private fun showAlertAndOperate(
@StringRes stringId: Int,
leave: Leave, func: () -> Unit
) {
leaveDetailsDialogFragment.dismiss()
val text = getString(stringId, leave.User.getNameSurname())
showAlertDialog(text, DialogInterface.OnClickListener {_, which ->
if (which == DialogInterface.BUTTON_POSITIVE) {
func() /* func() is sent as parameter. When dialog shows up
and user tap OK button, func() is fired here */
}
})
}“showAlertAndOperate” ismini verdiğim bu fonksiyon, 3 adet parametre alıyor. İlk parametresi bir Int ancak annotation olarak StringRes’e sahip. Yani bu fonksiyona göndereceğiniz ilk parametre R.string.xxx gibi bir string id olacak. İkinci parametre bir Leave POJO objesi. Son parametre ise işlemi yapacak olan fonksiyon. Yani; son parametrede eğer izin silme işlemi yapacaksak izin silme request’ini, izin onaylama işlemi yapacaksak onaylama request’ini yollayan fonksiyon çağırılacak.
“showAlertAndOperate” fonksiyonu, önce izin detay dialog fragment’ını kapatıyor. Ardından normal Dialog içerisinde gösterilecek olan metni StringRes ve Leave parametrelerini kullanarak hazırlıyor. Ardından bu metni showAlertDialog fonksiyonuna yollayarak Global alertdialog’un çıkmasını sağlıyor. Dialog’un onay butonuna basıldığı takdirde ise fonksiyona parametre olarak gönderilen fonksiyon çalışıyor.
override fun onLeaveApprove(leave: Leave) =
showAlertAndOperate(R.string.are_you_sure_to_accept, leave) {
confirmLeave(leave, MODE_ACCEPTED)
}
override fun onLeaveDecline(leave: Leave) =
showAlertAndOperate(R.string.are_you_sure_to_decline, leave) {
confirmLeave(leave, MODE_DECLINED)
}
override fun onLeaveDelete(leave: Leave) =
showAlertAndOperate(R.string.are_you_sure_to_remove, leave) {
deleteLeave(leave)
}Yukarıda gördüğünüz fonksiyonlar ise izin detay dialog fragment’ına ait interface içerisinde bulunan buton onClick işleminde çalışan callbackler. Gördüğünüz üzere; onLeaveApprove ‘da confirmLeave fonksiyonu accepted modunda, onLeaveDecline ‘da confirmLeave fonksiyonu declined modunda çağrılmış. “onDelete”te ise deleteLeave() fonksiyonu çağrılmış. “showAlertAndOperate” fonksiyonunda belirtilen func() fonksiyonu yerine bu fonksiyonlar çağrılacak.
Tabii kodu kopyala-yapıştır yapmamak için pek çok farklı yöntem de mevcut. Bu yazıda bu yöntemlerden sadece birinden bahsedebildim. “En güzel yöntem budur, golden key’dir,” gibi iddialara sahip değilim. Sadece bu seferlik bunu tercih ettim ve sizlerle paylaşmak istedim.
Bu yazı aslında daha önce çalıştığım bir şirket olan Peakup blog da yayınlanmıştır. Orada ekran görüntüleri bozulduğu ve yazının sahibinin ben olduğum belli olmadığı için buraya aktarmayı tercih ettim.
Bol kodlu günler dilerim.
Sıkça Sorulan Sorular
▸Yüksek seviye fonksiyon (higher-order function) ne demek?
Bir fonksiyonu parametre olarak kabul eden ya da başka bir fonksiyon döndüren fonksiyonlara yüksek seviye fonksiyon denir. Kotlin bu özelliği doğal olarak destekler.
▸Kotlin neden fonksiyonel dil özellikleri taşıyor?
Kotlin'de fonksiyonlar Java'nın aksine herhangi bir sınıf içinde olmak zorunda değildir. Fonksiyonlar değişkenlerde ve veri yapılarında tutulabilir; bu da Kotlin'i fonksiyonel programlamaya uygun kılar.
▸First class fonksiyon ne anlama gelir?
First class fonksiyon, fonksiyonların String veya Integer gibi diğer türlerle aynı seviyede tutulduğu anlamına gelir. Yani fonksiyonları parametre olarak gönderebilir, değişkene atayabilir veya return olarak döndürebilirsiniz.
▸Kotlin'de bir fonksiyona parametre olarak başka bir fonksiyon nasıl gönderilir?
Fonksiyon parametresi tanımlarken `function: () -> Unit` gibi bir imza kullanılır. Çağırırken lambda bloğu `{ }` içinde ya da `::fonksiyonAdı` şeklinde gönderilebilir.
▸Higher-order function Android geliştirmede ne işe yarar?
Farklı ekranlarda (Activity, Fragment) benzer işlemler yapılıyorsa aynı kodu tekrar tekrar yazmak yerine tek bir yüksek seviye fonksiyon yazılabilir. Bu yöntem kod tekrarını azaltır ve büyük projelerde yönetimi kolaylaştırır.
▸Kotlin'de kod tekrarını azaltmak için higher-order function nasıl kullanılır?
Yazar, izin silme, onaylama ve reddetme işlemleri için ayrı ayrı benzer fonksiyonlar yazmak yerine `showAlertAndOperate` adında tek bir higher-order function yazmış. Hangi işlemin yapılacağını belirleyen fonksiyon ise parametre olarak gönderilmiş.
▸Kotlin'de higher-order function ile lambda arasındaki fark nedir?
Lambda, fonksiyona parametre olarak geçilebilen isimsiz bir fonksiyon bloğudur. Higher-order function ise bu lambdaları veya diğer fonksiyonları parametre olarak kabul eden üst düzey fonksiyonun kendisidir; ikisi birlikte kullanılır.
▸Kotlin neden Android'in resmi dili oldu?
Google IO 2017'de Kotlin, Android için resmi programlama dili ilan edildi. Bu kararın arka planında Oracle ile Google arasındaki Java lisans davası da etkili olmuştur.
▸Higher-order function kullanmak kodun kalitesini nasıl etkiler?
Kod tekrarını ortadan kaldırır, okunabilirliği artırır ve ileride yapılacak değişiklikleri kolaylaştırır. Yazar, bu yaklaşımı kullanmadan önce kopyala-yapıştır yöntemiyle büyük projelerde yönetim zorluğu yaşandığını vurguluyor.


