Kotlin Yazıları #3 Fonksiyonlar

Fonksiyonlar

Bu yazının konusu Fonksiyonlar olacak.

Fun anahtar kelimesi kullanılarak üretilirler. Fun kelimesinden sonraki kelime ( parantezine kadar fonksiyon ismini gösterir.  a: Int ve b: Int değişken isimleri ve tiplerini, : Int return tipini gösterir.

Kullanımı :

val result = sum(2, 5)
fun sum(a: Int, b: Int): Int {
     return a + b
}

Daha kısa hali :

fun sum(a: Int, b: Int) = a + b

return tipine sahip olmayan :

fun printSum(a: Int, b: Int): Unit { // : Unit düşürülebilir.
   println("sum of $a and $b is ${a + b}")
}

Başka Class larda bulunan fonksiyonlar şu şekilde çağırılır :

Sample().foo() // Sample sınıfından bir instance yaratır ve foo fonksiyonunu çağırır.

Parametreler name: type şeklinde tanımlanır.

Default değerler = kullanılarak tanımlanır. Override edilen methodlar daima bu default değeri kullanırlar. Ancak override dilen methodlar bu default değeri gösteremezler.

open class A {
    open fun foo(i: Int = 10) { ... }
}
class B : A() {
    override fun foo(i: Int) { ... } // no default value allowed
 }

Eğer bir fonksiyon fazla sayıda parametreye sahipse, fonksiyonu çağırabilmek için parametrelerin hepsini fonksiyona göndermek zorunda değiliz.

fun reformat(str: String,
                       normalizeCase: Boolean = true,
                       upperCaseFirstLetter: Boolean = true,
                       divideByCamelHumps: Boolean = false,
                       wordSeparator: Char = ' ') { ... }

Bu fonksiyonu şu şekillerde çağırabiliriz:

reformat(str)

Ancak default değerler olmazsa bu fonksiyonu ancak şu şekilde çağırabiliriz:

reformat(str, true, true, false, '_')

Aklınıza gelen soruyu duyar gibi oluyorum. Hemen cevap vereyim :

reformat(str, wordSeparator = '_')

Tek-İfade fonksiyonları (Single-Expression)

fun double(x: Int) = x * 2

 

Varargs (Variable numberof arguments) yapısı

Tek bir methoda aynı tipte birden fazla parametre göndereceksek bu tipi kullanmalıyız.

fun <T> asList(vararg ts: T): List<T> {
     val result = ArrayList<T>()
     for (t in ts) // ts is an Array
     result.add(t) return result
}

Bu fonksiyonunu çağırılışı şu şekildedir:

val list = asList(1, 2, 3)

 

Yerel fonksiyonlar

Kotlin iç içe fonksiyonları desteklemektedir. Ve içerideki fonksiyon dışarıdaki fonksiyonun değişkenlerine ulaşabilmektedir.

fun dfs(graph: Graph) {
     val visited = HashSet<Vertex>()
     fun dfs(current: Vertex) {
          if (!visited.add(current)) return
          for (v in current.neighbors) dfs(v)
     }
     dfs(graph.vertices[0])
}

Infix gösterimi :

Fonksiyonu bir aritmetik operatör gibi kullanmamızı sağlar. Böylece noktalara ve parantezlere ihtiyaç duymayız. Hemen örneğini iliştireyim :

infix fun createPyramid(rows: Int) {
      var k = 0
      for (i in 1..rows) {
            k = 0
            for (space in 1..rows-i) {
                  print("  ")
             }
        while (k != 2*i-1) {
              print("* ")
                   ++k
         }
        println()
        }
    }


fun main(args: Array<String>) {
    val p = Structure()
    p createPyramid 4       // p.createPyramid(4) yazmak yerine
}

Çıktı şu şekilde olacaktır :

      *
    * * *
  * * * * *
* * * * * * *

 

Generic Fonsiyonlar

Java Generic yapısının aynısını taşımaktadır.

fun <T> singletonList(item: T): List<T> {
     // ...
}

 

Üye Fonksiyonlar (Member)

Sınıf veya obje içerisinde yazılmış sınıflardır.

class Sample() {
     fun foo() {
          print("Foo")
     }
}

Kullanımı :

Sample().foo() // creates instance of class Sample and calls foo

 

İlave Fonksiyonlar (Extension Functions)

Bu tip fonksiyonlar Android uygulama geliştirme dünyasında bize pek çok yardımı olabilecek olan fonksiyonlardır. Ne yazık ki Android framework bazen işleri gereğinden fazla zorlaştırabiliyor ve Java’da bulabildiğimiz tek çare kendi wrapper larımızı veya static methodlarla dolu utility sınıflarımızı yazmak oluyor. Bu da okunabilirlik açısından çok da iyi bir çözüm olmuyor. İşte burada Kotlin’in özelliği olan Extension Fonksiyonlar yardımımıza Framework sınıflarına ekleme yaparak koşuyor.

Kısaca Extension Fonksiyonlar, Framework sınıflarının kendi kodlarına dokunmadan bazı fonksiyonalitelerini değiştirebilmemize olanak sağlıyor.

View sınıfına, visible isimli view’ı visible yapacak bir fonksiyon lazım olsun.

fun View.visible() {
     visibility = View.VISIBLE
}

İşte bu kadar.

Android dünyasından birkaç örnek daha vermek istiyorum bu konuda. Dikkat Android dünyasından vereceğim örnekler için Android programlamayı biraz kurcalamış olmanızı tavsiye ederim.

Adapter sınıfları içerisinde bir view’ı inflate edebilmek için yazdığımız kod şudur :

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
     val v = LayoutInflater.from(parent.context).inflate(R.layout.view_item, parent, false)
     return ViewHolder(v)
}

Ancak neden ViewGroup sınıfına inflate özelliğini katmayalım ki?

fun ViewGroup.inflate(layoutRes: Int): View {
     return LayoutInflater.from(context).inflate(layoutRes, this, false)
}

Ve bu fonksiyonun kullanımı :

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
     val v = parent.inflate(R.layout.view_item)
     return ViewHolder(v)
}

Çok basit bir örnek daha vereyim. Eğer Picasso kütüphanesini kullanıyorsanız bilirsiniz :

Picasso.with(imageView.context).load(url).into(imageView)

Peki bunu ImageView sınıfına eklemek neden mümkün olmasın ? Adeta ImageView sınıfının içine loadUrl isimli bir method gömdüğünüzü ve başka hiçbir methoda dokunmadığınızı düşünün. Utility sınıfı yok. Static method yok. extends ImageView yok. Tek bir methodu herhangi bir isme sahip herhangi bir sınıfta bu şekilde yazdığınız anda koca ImageView sınıfına bir method eklemiş oluyorsunuz.

fun ImageView.loadUrl(url: String) {
     Picasso.with(context).load(url).into(this)
}

Ve kullanımı şu şekilde

imageView.loadUrl(url)

Çok iyi değil mi Ey Android uygulama geliştiricileri? Başta Erdem abi? :D

Gelecek yazılarda Android referansları çok daha fazla olacak. Ve tabiiki Kotlin kullanarak 0dan bir Android projesi yazacağım bu blog yazılarında. Kotlin fonksiyonlarından en sonununcusuna gelelim:

Kuyruk Tekrarlı Fonksiyonlar (Tail Recursion Functions)

Bazı algoritmalar Recursive fonksiyonlar kullanmadan loop yapısına ihtiyaç duyarlar. Stack overflow riski olmadan Kotlin’de bize bunu sağlayan yapı tailrec yapısıdır. Fonksiyon tanımlarının başına ekleyerek bu yapıyı kullanabiliriz.

Geleneksel yolda şu şekilde yazılan recursive fonksiyon :

private fun findFixPoint(): Double {
     var x = 1.0
     while (true) {
          val y = Math.cos(x)
          if (x == y) return y
          x = y
     }
}

Kotlin’in tailrec özelliği sayesinde şuna dönüşür:

tailrec fun findFixPoint(x: Double = 1.0): Double = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

Bu fonksiyon cosinus değerini hesaplayan bir fonksiyondur. Sonuç değişmez olana kadar çalışmaya devam eder. Kısaca recursivedir. Tailrec kullanabilmek için fonksiyon son işlem olarak kendisini çağırmak zorundadır. Try catch finally yapılarında kullanılamaz. Şu anda sadece JVM backendinde kullanılan bir yapıdır.

Ayrıca: Java, C# veya Scala gibi dillerde fonksiyonları sınıfların içerisine yazmak zorundasınız. Ancak Kotlin’de fonksiyonları sınıflar içerisine yazmak zorunda değilsiniz. Yani ille bir class oluşturup fonksiyonları onun içine yazmanız gerekmiyor.

Blog yazılarına abone olmak ister misiniz ?

Her yeni blog yazısı çıktığında veya soru yayınlandığında bildirim almak için e-mail adresinizi yazmanız yeterli.