Kotlin programlama dilini öğrenin

Kotlin, her yerde Android geliştiricileri tarafından yaygın olarak kullanılan bir programlama dilidir. Bu konu, kısa sürede hazırlayıp kullanmaya başlamanızı sağlayacak bir Kotlin atlama kursu işlevi görüyor.

Değişken bildirimi

Kotlin, değişkenleri bildirmek için iki farklı anahtar kelime kullanır: val ve var.

  • Değeri asla değişmeyen bir değişken için val kullanın. Bir değeri, val kullanılarak tanımlanan bir değişkene yeniden atayamazsınız.
  • Değeri değişebilen bir değişken için var kullanın.

Aşağıdaki örnekte count, başlangıç değeri 10 olan Int türünde bir değişkendir:

var count: Int = 10

Int, Kotlin'de temsil edilebilen birçok sayısal türden biri olan tam sayıyı temsil eden bir türdür. Diğer dillere benzer şekilde, sayısal verilerinize bağlı olarak Byte, Short, Long, Float ve Double dillerini de kullanabilirsiniz.

var anahtar kelimesi, değerleri gerektiği gibi count öğesine yeniden atayabileceğiniz anlamına gelir. Örneğin, count değerini 10 yerine 15 olarak değiştirebilirsiniz:

var count: Int = 10
count = 15

Yine de bazı değerlerin değiştirilmesi amaçlanmamıştır. languageName adlı bir String kullanmayı düşünün. languageName parametresinin her zaman "Kotlin" değerine sahip olmasını istiyorsanız val anahtar kelimesini kullanarak languageName özelliğini belirtebilirsiniz:

val languageName: String = "Kotlin"

Bu anahtar kelimeler, nelerin değiştirilebileceğini açıkça belirtmenize olanak tanır. Bunları kendi yararınıza kullanabilirsiniz. Bir değişken referansının yeniden atanabilir olması gerekiyorsa bunu var olarak bildirin. Aksi takdirde val alanını kullanın.

Çıkarımı yazın

Önceki örnekten devam edersek languageName öğesine başlangıç değeri atadığınızda Kotlin derleyicisi, atanan değerin türüne göre türü tahmin edebilir.

"Kotlin" değeri String türünde olduğundan derleyici, languageName değerinin aynı zamanda bir String olduğu sonucuna varır. Kotlin'in statik olarak yazılmış bir dil olduğunu unutmayın. Bu, türün derleme zamanında çözümlendiği ve hiçbir zaman değişmediği anlamına gelir.

Aşağıdaki örnekte languageName, String olarak tahmin edildiği için String sınıfının parçası olmayan işlevleri çağıramazsınız:

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

toUpperCase(), yalnızca String türündeki değişkenlerde çağrılabilen bir işlevdir. Kotlin derleyicisi languageName değerini String olarak tahmin ettiği için toUpperCase() yöntemini güvenle çağırabilirsiniz. Ancak inc(), Int operatör işlevi olduğundan String üzerinde çağrılamaz. Kotlin'in tür çıkarıma yaklaşımı size hem kısa netlik hem de tür güvenliği sağlar.

Boş güvenlik

Bazı dillerde, referans türü değişkeni başlangıçtaki açık bir değer sağlanmadan bildirilebilir. Bu durumlarda, değişkenler genellikle null değer içerir. Kotlin değişkenleri varsayılan olarak boş değerler içeremez. Bu, aşağıdaki snippet'in geçersiz olduğu anlamına gelir:

// Fails to compile
val languageName: String = null

Bir değişkenin null değer alabilmesi için nullable türünde olması gerekir. Bir değişkeni aşağıdaki örnekte gösterildiği gibi, türünü ? ile ekleyerek boş değer atanabilir olarak belirtebilirsiniz:

val languageName: String? = null

String? türü sayesinde languageName adlı kullanıcıya String değeri veya null atayabilirsiniz.

Boş değerli değişkenleri dikkatli bir şekilde ele almanız gerekir. Aksi halde, korkunç bir NullPointerException riski söz konusu olabilir. Örneğin, Java'da bir yöntemi null değeriyle çağırmaya çalışırsanız programınız kilitlenir.

Kotlin, null değişkenlerle güvenli bir şekilde çalışmak için çeşitli mekanizmalar sunar. Daha fazla bilgi için Android'de Yaygın Kotlin kalıpları: Nullability bölümüne bakın.

Koşullar

Kotlin, koşullu mantığı uygulamak için çeşitli mekanizmalar içerir. Bunların en yaygını if-else ifadesidir. Bir if anahtar kelimesinin yanında parantez içine alınmış bir ifade true olarak değerlendirilirse bu dalın içindeki kod (ör. hemen ardından gelen, süslü parantez içine sarmalanmış kod) yürütülür. Aksi takdirde, else dalı içindeki kod yürütülür.

if (count == 42) {
    println("I have the answer.")
} else {
    println("The answer eludes me.")
}

Birden çok koşulu else if kullanarak temsil edebilirsiniz. Bu, aşağıdaki örnekte gösterildiği gibi tek bir koşullu ifadede daha ayrıntılı ve karmaşık bir mantığı temsil etmenize olanak tanır:

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

Koşullu ifadeler, durum bilgili mantığı temsil etmek için kullanışlıdır ancak bunları yazarken kendinizi tekrarlayabilirsiniz. Yukarıdaki örnekte, her dalda bir String yazdırmanız yeterlidir. Kotlin bu tekrarı önlemek için koşullu ifadeler sunar. Son örnek şu şekilde yeniden yazılabilir:

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

Dolaylı olarak, her koşullu dal, ifadenin sonucunu son satırında döndürür, böylece return anahtar kelimesi kullanmanıza gerek yoktur. Üç dalın tamamının sonucu String türünde olduğundan if-else ifadesinin sonucu da String türünde olur. Bu örnekte, answerString öğesine if-else ifadesinin sonucundan bir başlangıç değeri atanır. Tür çıkarımı, answerString için açık tür bildirimini atlamak için kullanılabilir ancak açıklık sağlamak için genellikle bunu dahil etmek iyi bir fikirdir.

Koşullu ifadenizin karmaşıklığı arttıkça, if-else ifadenizi aşağıdaki örnekte gösterildiği gibi when ifadesiyle değiştirmeyi düşünebilirsiniz:

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

when ifadesindeki her dal; bir koşul, ok (->) ve bir sonuçla temsil edilir. Okun sol tarafındaki koşul doğru olarak değerlendirilirse sağ taraftaki ifadenin sonucu döndürülür. Yürütmenin bir daldan diğerine geçmediğini unutmayın. when ifade örneğindeki kod, işlevsel olarak önceki örnektekiyle eşdeğerdir ancak muhtemelen daha kolay okunur.

Kotlin’in koşullu özellikleri, en güçlü özelliklerinden biri olan akıllı yayınlama özelliğini öne çıkarır. Boş değerlerle çalışmak için güvenli arama operatörünü veya null olmayan onay operatörünü kullanmak yerine, bunun yerine, aşağıdaki örnekte gösterildiği gibi bir değişkenin koşullu ifade kullanarak null değerine referans içerip içermediğini kontrol edebilirsiniz:

val languageName: String? = null
if (languageName != null) {
    // No need to write languageName?.toUpperCase()
    println(languageName.toUpperCase())
}

Koşullu dal içinde languageName, boş değer atanamaz olarak değerlendirilebilir. Kotlin, dalı yürütme koşulunun, languageName null değer içermemesi olduğunu kabul edecek kadar akıllıdır. Bu nedenle, languageName öğesini bu dal içinde null özellikli olarak değerlendirmeniz gerekmez. Bu akıllı yayınlama boş kontroller, tür kontrolleri veya bir sözleşmeyi karşılayan tüm koşullar için çalışır.

Fonksiyonlar

Bir veya daha fazla ifadeyi bir işlev halinde gruplandırabilirsiniz. Bir sonuca ihtiyacınız olduğunda her defasında aynı ifade dizisini tekrarlamak yerine, ifadeleri bir fonksiyon içinde sarmalayabilir ve bunun yerine o işlevi çağırabilirsiniz.

Bir işlevi tanımlamak için fun anahtar kelimesini ve ardından işlev adını kullanın. Ardından, işlevinizin aldığı giriş türlerini (varsa) ve döndürdüğü çıkış türünü tanımlayın. Bir işlevin gövdesinde, işleviniz çağrıldığında çağrılan ifadeleri tanımladığınız yerdir.

Önceki örneklere dayanarak, eksiksiz bir Kotlin işlevi görebilirsiniz:

fun generateAnswerString(): String {
    val answerString = if (count == 42) {
        "I have the answer."
    } else {
        "The answer eludes me"
    }

    return answerString
}

Yukarıdaki örnekte bulunan işlev generateAnswerString adına sahiptir. Herhangi bir girdi gerektirmez. String türünde bir sonuç verir. Bir işlevi çağırmak için işlevin adını ve ardından çağrı operatörünü (()) kullanın. Aşağıdaki örnekte answerString değişkeni, generateAnswerString() sonucuyla başlatılır.

val answerString = generateAnswerString()

İşlevler, aşağıdaki örnekte gösterildiği gibi girdi olarak bağımsız değişkenler alabilir:

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

Bir işlevi tanımlarken, istediğiniz sayıda bağımsız değişken ve bunların türlerini belirtebilirsiniz. Yukarıdaki örnekte generateAnswerString(), Int türünde countThreshold adlı bir bağımsız değişkeni alır. İşlev içinde, adını kullanarak bağımsız değişkene başvurabilirsiniz.

Bu işlevi çağırırken işlev çağrısının parantezlerinin içine bir bağımsız değişken eklemeniz gerekir:

val answerString = generateAnswerString(42)

Fonksiyon ifadelerini basitleştirme

generateAnswerString() oldukça basit bir işlevdir. İşlev bir değişken tanımlar ve hemen geri döner. Tek bir ifadenin sonucu bir işlevden döndürüldüğünde, aşağıdaki örnekte gösterildiği gibi, işlevde yer alan if-else ifadesinin sonucunu doğrudan döndürerek yerel değişken bildirmeyi atlayabilirsiniz:

fun generateAnswerString(countThreshold: Int): String {
    return if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }
}

Return anahtar kelimesini atama operatörüyle de değiştirebilirsiniz:

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }

Anonim işlevler

Her işlevin adı yoktur. Bazı fonksiyonlar girdi ve çıktılarıyla daha doğrudan tanımlanır. Bu işlevlere anonim işlevler denir. Daha sonra anonim işlevi çağırmak için bu referansı kullanarak anonim bir işleve referans tutabilirsiniz. Referansı, diğer referans türlerinde olduğu gibi uygulamanızın etrafında da aktarabilirsiniz.

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

Adlandırılmış işlevlerde olduğu gibi anonim işlevler de istediğiniz sayıda ifade içerebilir. İşlevin döndürülen değeri, son ifadenin sonucudur.

Yukarıdaki örnekte stringLengthFunc, giriş olarak String alan ve String girdisinin uzunluğunu Int türünün çıktısı olarak döndüren anonim bir işleve referans içerir. Bu nedenle, işlevin türü (String) -> Int olarak belirtilir. Ancak bu kod işlevi çağırmaz. İşlevin sonucunu almak için, söz konusu işlevi adlandırılmış bir işlevde olduğu gibi çağırmanız gerekir. stringLengthFunc çağırırken aşağıdaki örnekte gösterildiği gibi bir String sağlamanız gerekir:

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

val stringLength: Int = stringLengthFunc("Android")

Üst düzey işlevler

Bir işlev, başka bir işlevi bağımsız değişken olarak alabilir. Bağımsız değişken olarak diğer işlevleri kullanan işlevler, üst düzey işlevler olarak adlandırılır. Bu kalıp, Java'da geri çağırma arayüzünü kullandığınız şekilde bileşenler arasında iletişim kurmak için kullanışlıdır.

Aşağıda, üst düzey bir işleve ilişkin bir örnek verilmiştir:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

stringMapper() işlevi, bir String işlevini, ilettiğiniz bir String öğesinden Int değeri türeten bir işlevle birlikte alır.

Aşağıdaki örnekte gösterildiği gibi, bir String ve diğer giriş parametresini karşılayan bir işlev (giriş olarak String alıp Int döndüren bir işlev) ileterek stringMapper() çağırabilirsiniz:

stringMapper("Android", { input ->
    input.length
})

Anonim işlev, bir işlevde tanımlanan last parametresiyse bu parametreyi, aşağıdaki örnekte gösterildiği gibi, işlevi çağırmak için kullanılan parantezlerin dışına geçirebilirsiniz:

stringMapper("Android") { input ->
    input.length
}

Kotlin standart kitaplığında anonim işlevler bulunabilir. Daha fazla bilgi için Yüksek Sıralı İşlevler ve Lambda'lar bölümüne bakın.

Sınıflar

Şimdiye kadar bahsi geçen türlerin tümü Kotlin programlama dilinde yerleşiktir. Kendi özel türünüzü eklemek isterseniz aşağıdaki örnekte gösterildiği gibi class anahtar kelimesini kullanarak bir sınıf tanımlayabilirsiniz:

class Car

Kod

Sınıflar, özellikleri kullanarak durumu temsil eder. Özellik; alıcı, setter ve geri alan içerebilen sınıf düzeyinde bir değişkendir. Bir arabada sürüş için tekerlek kullanılması gerektiğinden, aşağıdaki örnekte gösterildiği gibi Wheel nesnelerinin bir listesini Car özelliği olarak ekleyebilirsiniz:

class Car {
    val wheels = listOf<Wheel>()
}

wheels öğesinin bir public val olduğunu, yani wheels öğesine Car sınıfının dışından erişilebildiğini ve yeniden atanamayacağını unutmayın. Car örneği elde etmek istiyorsanız önce kurucusunu çağırmanız gerekir. Buradan erişilebilir özelliklerine erişebilirsiniz.

val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

Tekerleklerinizi özelleştirmek istiyorsanız sınıf özelliklerinin nasıl başlatılacağını belirten bir özel oluşturucu tanımlayabilirsiniz:

class Car(val wheels: List<Wheel>)

Yukarıdaki örnekte, sınıf oluşturucu, List<Wheel> özelliğini kurucu bağımsız değişkeni olarak alır ve wheels özelliğini başlatmak için bu bağımsız değişkeni kullanır.

Sınıf işlevleri ve kapsülleme

Sınıflar, davranışı modellemek için işlevleri kullanır. Functions, durumu değiştirerek yalnızca göstermek istediğiniz verileri göstermenize yardımcı olur. Bu erişim denetimi, kapsülasyon olarak bilinen daha geniş nesne odaklı bir kavramın parçasıdır.

Aşağıdaki örnekte doorLock özelliği, Car sınıfının dışındaki her şeyden gizli tutulur. Arabanın kilidini açmak için aşağıdaki örnekte gösterildiği gibi geçerli bir anahtarda geçen unlockDoor() işlevini çağırmanız gerekir:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

Bir mülke referans verildiğini özelleştirmek isterseniz özel alıcı ve ayarlayıcı sağlayabilirsiniz. Örneğin, bir mülkün alıcısını, ayarlayıcısına erişimi kısıtlarken açığa çıkarmak isterseniz bu ayarlayıcıyı private olarak tanımlayabilirsiniz:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    var gallonsOfFuelInTank: Int = 15
        private set

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

Özelliklerin ve işlevlerin bir kombinasyonunu kullanarak her tür nesneyi modelleyen sınıflar oluşturabilirsiniz.

Birlikte çalışabilirlik

Kotlin’in en önemli özelliklerinden biri, Java ile akıcı bir şekilde birlikte çalışabilmesidir. Kotlin kodu JVM bayt koduna göre derlendiğinden Kotlin kodunuz doğrudan Java kodunu, bunun tersi de geçerlidir. Yani mevcut Java kitaplıklarından doğrudan Kotlin'den yararlanabilirsiniz. Dahası, Android API'lerinin çoğu Java'da yazılır ve doğrudan Kotlin'den çağırabilirsiniz.

Sonraki adımlar

Kotlin, sunduğu destek ve ivmeyle esnek ve pragmatik bir dildir. Henüz denemediyseniz bunu denemenizi öneririz. Sonraki adımlar için Android uygulamalarınızda yaygın Kotlin kalıplarını uygulama kılavuzuyla birlikte resmi Kotlin dokümanlarına göz atın.