Programcılar için Kotlin Eğitim Programı 5.1: Uzantılar

Bu codelab'de, Programcılar için Kotlin Eğitim Programı kursunun bir parçası bulunmaktadır. Codelab'ler üzerinden sırayla çalışıyorsanız bu kurstan en iyi şekilde yararlanabilirsiniz. Bilginize bağlı olarak bazı bölümlere göz atabilirsiniz. Bu kurs, nesneye duyarlı bir dil bilen ve Kotlin öğrenmek isteyen programcılara yöneliktir.

Giriş

Bu codelab'de, Kotlin'de çiftler, koleksiyonlar ve uzantı işlevleri dahil olmak üzere çeşitli faydalı özellikler tanıtıyorsunuz.

Bu kurstaki dersler, tek bir örnek uygulama oluşturmak yerine bilginizi geliştirmek için tasarlanmıştır ancak birbirinizden yarı bağımsızdır. Böylece aşina olduğunuz bölümlere göz atabilirsiniz. Hepsini bir araya getirmek için, örneklerin çoğu akvaryum teması kullanır. Akvaryum hikayesinin tamamını görmek isterseniz Programcılar için Kotlin Eğitim Programı Udacity'deki kursa göz atabilirsiniz.

Bilmeniz gerekenler

  • Kotlin işlevleri, sınıfları ve yöntemlerinin söz dizimi
  • IntelliJ IDEA'da Kotlin's REPL (Read-Eval-Print Loop) ile çalışma
  • IntelliJ IDEA'da yeni sınıf oluşturma ve program çalıştırma

Neler öğreneceksiniz?

  • Çiftler ve üçlülerle çalışma
  • Koleksiyonlar hakkında daha fazla bilgi
  • Sabitleri tanımlama ve kullanma
  • Uzantı işlevleri yazma

Yapacaklarınız

  • REPL'deki çiftler, üçlüler ve karma haritalar hakkında bilgi edinin
  • Sabit değerleri düzenlemenin farklı yollarını öğrenin
  • Bir uzantı işlevi ve uzantı özelliği yazın

Bu görevde çiftleri ve üçlüleri öğrenip yıkma konusunda bilgi edinirsiniz. Çiftler ve üçlüler, 2 veya 3 genel öğe için önceden hazırlanmış veri sınıflarıdır. Bu, örneğin bir işlevin birden fazla değer döndürmesini sağlamak için yararlı olabilir.

Bir List balıklarınız ve balığın tatlı su veya tuzlu su balığı olup olmadığını kontrol etmek için bir isFreshWater() işlevine sahip olduğunuzu varsayalım. List.partition(), biri koşulun true, diğerinin koşulun false olduğu öğelere sahip iki liste döndürür.

val twoLists = fish.partition { isFreshWater(it) }
println("freshwater: ${twoLists.first}")
println("saltwater: ${twoLists.second}")

1. Adım: Birkaç çift ve üçlü yapın

  1. REPL'i açın (Araçlar > Kotlin > Kotlin REPL).
  2. Kullanılan donanımla bir ekipman parçasını ilişkilendirerek bir çift oluşturun, ardından değerleri yazdırın. İki dize gibi iki değeri to anahtar kelimesiyle bağlayan bir ifade oluşturarak, ardından her bir değere başvuruda bulunmak için .first veya .second ifadesini kullanarak çift oluşturabilirsiniz.
val equipment = "fish net" to "catching fish"
println("${equipment.first} used for ${equipment.second}")
⇒ fish net used for catching fish
  1. Üçlü olarak oluşturup toString() ile yazdırın, ardından toList() ile bir listeye dönüştürün. Üç değer kullanarak Triple() kullanarak üçlü oluşturursunuz. Her bir değeri ifade etmek için .first, .second ve .third ifadelerini kullanın.
val numbers = Triple(6, 9, 42)
println(numbers.toString())
println(numbers.toList())
⇒ (6, 9, 42)
[6, 9, 42]

Yukarıdaki örnekler, çiftin veya üçgenin tüm bölümleri için aynı türü kullanır ancak bu zorunlu değildir. Parçalar bir dize, bir sayı veya liste olabilir. Örneğin, başka bir çift veya üçlü.

  1. Çiftin ilk bölümünün çift olduğu bir çift oluşturun.
val equipment2 = ("fish net" to "catching fish") to "equipment"
println("${equipment2.first} is ${equipment2.second}\n")
println("${equipment2.first.second}")
⇒ (fish net, catching fish) is equipment
⇒ catching fish

2. Adım: Bazı çiftleri ve üçlüleri yok edin

Çiftleri ve üçlü parçaları parçalarına ayırmaya yıkma adı verilir. Çifti veya üç katına uygun sayıda değişken atayın. Kotlin her bir bölümün değerini sırayla atar.

  1. Çifti yok edin ve değerleri yazdırın.
val equipment = "fish net" to "catching fish"
val (tool, use) = equipment
println("$tool is used for $use")
⇒ fish net is used for catching fish
  1. Üçlü yıkım ve değerleri yazdırın.
val numbers = Triple(6, 9, 42)
val (n1, n2, n3) = numbers
println("$n1 $n2 $n3")
⇒ 6 9 42

Çiftler ve üçlülerin yok edilmesi, önceki bir codelab'de ele alınan veri sınıflarıyla aynı şekilde çalışır.

Bu görevde listeler ve yeni bir koleksiyon türü olan karma eşlemeler dahil olmak üzere koleksiyonlar hakkında daha fazla bilgi edinebilirsiniz.

1. Adım: Listeler hakkında daha fazla bilgi edinin

  1. Listeler ve yoksayılabilir listeler önceki bir derste kullanıma sunulmuştur. Bunlar, oldukça kullanışlı bir veri yapısı olduğundan Kotlin, listeler için çeşitli yerleşik işlevler sunar. Listeler için işlevlerin bu kısmi listesini inceleyin. List ve MutableList için Kotlin dokümanlarında tam girişler bulabilirsiniz.

İşlev

Amaç

add(element: E)

Değiştirilebilir listeye öğe ekleyin.

remove(element: E)

Dönüştürülebilir listeden bir öğe kaldırın.

reversed()

Öğelerin ters sırada listelendiği bir liste kopyasını döndürün.

contains(element: E)

Liste öğeyi içeriyorsa true değerini döndürün.

subList(fromIndex: Int, toIndex: Int)

Listenin ilk dizininden ikinci dizinine kadar olan kısmını tekrar ekleyin.

  1. Hâlâ REPL üzerinde çalışıyorsa bir numara listesi oluşturun ve ona sum() çağrısı yapın. Bu işlem tüm öğeleri özetler.
val list = listOf(1, 5, 3, 4)
println(list.sum())
⇒ 13
  1. Dize listesi oluşturun ve listeyi toplayın.
val list2 = listOf("a", "bbb", "cc")
println(list2.sum())
⇒ error: none of the following functions can be called with the arguments supplied:
  1. Öğe, List öğesinin doğrudan nasıl toplanacağını bildiği bir şey (ör. bir dize) varsa lambda işleviyle .sumBy() kullanarak bunun nasıl toplanacağını (örneğin, her dizenin uzunluğunun toplamına) belirtebilirsiniz. Bir lambda bağımsız değişkeni için varsayılan ad it'tir. Burada it, listede gezinirken gerçekleştirilen her bir öğeyi ifade eder.
val list2 = listOf("a", "bbb", "cc")
println(list2.sumBy { it.length })
⇒ 6
  1. Listelerle yapabileceğiniz daha pek çok şey var. Kullanılabilir işlevselliği görmenin yollarından biri, IntelliJ IDEA'da bir liste oluşturmak, noktayı eklemek ve ardından ipucundaki otomatik tamamlama listesine bakmaktır. Bu, tüm nesneler için kullanılabilir. Bir listeyle deneyin.

  1. Listeden listIterator() seçin, ardından listede for ifadesiyle gezinin ve tüm öğeleri boşluklarla ayırarak yazdırın.
val list2 = listOf("a", "bbb", "cc")
for (s in list2.listIterator()) {
    println("$s ")
}
⇒ a bbb cc

2. Adım: Karma eşlemelerini deneyin

Kotlin'de hashMapOf() özelliğini kullanarak neredeyse her şeyi eşleyebilirsiniz. Karma haritalar, ilk değerin anahtar görevi gördüğü bir çift listesi gibidir.

  1. Balıkların belirtileri, anahtarları ve hastalıkları ve değerleriyle eşleşen bir karma haritası oluşturun.
val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
  1. Ardından, get() veya hatta daha kısa, köşeli parantez [] kullanarak belirti anahtarına göre hastalık değerini alabilirsiniz.
println(cures.get("white spots"))
⇒ Ich
println(cures["red sores"])
⇒ hole disease
  1. Haritada olmayan bir belirti belirtmeyi deneyin.
println(cures["scale loss"])
⇒ null

Haritada bir anahtar yoksa eşleşen hastalığa dönüş yapmaya çalışmak null döndürür. Harita verilerine bağlı olarak, olası bir anahtarla eşleşmemek yaygın bir durumdur. Böyle durumlarda Kotlin, getOrDefault() işlevini sağlar.

  1. Eşleşmeyen bir anahtarı getOrDefault() kullanarak aramayı deneyin.
println(cures.getOrDefault("bloating", "sorry, I don't know"))
⇒ sorry, I don't know

Kotlin, bir değer döndürmekten fazlasını yapmanız gerekiyorsa getOrElse() işlevini sağlar.

  1. Kodunuzu getOrDefault() yerine getOrElse() kullanacak şekilde değiştirin.
println(cures.getOrElse("bloating") {"No cure for this"})
⇒ No cure for this

Basit bir varsayılan değer döndürmek yerine, süslü ayraçlar arasındaki kod {}. Örnekte, else bir dize döndürür, ancak tedavisi olan bir web sayfasını bulup döndürmek kadar eğlenceli olabilir.

mutableListOf gibi, mutableMapOf da yapabilirsiniz. Değişebilir harita, öğeleri koymanıza ve kaldırmanıza olanak sağlar. Değişmezlik, yalnızca değiştirilebildiği, değişmediği anlamına gelir.

  1. Bir ekipman dizesini öğe sayısıyla eşleyerek değiştirilebilen bir envanter haritası oluşturun. İçindeki balık ağlarıyla oluşturun, ardından put() ile envantere 3 tank çöpü ekleyin ve remove() ile balık ağını kaldırın.
val inventory = mutableMapOf("fish net" to 1)
inventory.put("tank scrubber", 3)
println(inventory.toString())
inventory.remove("fish net")
println(inventory.toString())
⇒ {fish net=1, tank scrubber=3}{tank scrubber=3}

Bu görevde Kotlin'deki sabit değerler ve bunları düzenlemenin farklı yöntemleri hakkında bilgi edineceksiniz.

1. Adım: Kontrast ve val hakkında bilgi edinin

  1. REPL'de sayısal bir sabit değer oluşturmayı deneyin. Kotlin'de const val düzeyinde üst düzey sabitler oluşturup derleme zamanında bir değer atayabilirsiniz.
const val rocks = 3

Değer atanır ve değiştirilemez. Bu, normal bir val belirtmek gibi bir şeydir. Peki const val ile val arasındaki fark nedir? const val değeri, derleme sırasında belirlenir. Burada val değeri program yürütülürken belirlenirken val işlevi, çalışma sırasında bir işlev tarafından atanabilir.

Yani val, bir işlevden değer atanabilir ancak const val atanamaz.

val value1 = complexFunctionCall() // OK
const val CONSTANT1 = complexFunctionCall() // NOT ok

Ayrıca const val yalnızca en üst düzeyde ve normal sınıflarla değil, object ile belirtilen tekli sınıflarda çalışır. Bunu, yalnızca sabit değerler içeren bir dosya veya tekli nesne oluşturmak için kullanabilir ve gerektiğinde içe aktarabilirsiniz.

object Constants {
    const val CONSTANT2 = "object constant"
}
val foo = Constants.CONSTANT2

2. Adım: Tamamlayıcı nesne oluşturma

Kotlin kavramının sınıf düzeyi sabitleri yoktur.

Bir sınıfın içindeki sabit değerleri tanımlamak için bunları sabit anahtar kelimelerle companion anahtar kelimesiyle sarmalamanız gerekir. Tamamlayıcı nesne, esasen sınıftaki tek bir nesnedir.

  1. Dize sabit değeri içeren bir tamamlayıcı nesne içeren sınıf oluşturun.
class MyClass {
    companion object {
        const val CONSTANT3 = "constant in companion"
    }
}

Tamamlayıcı nesneler ve normal nesneler arasındaki temel fark şudur:

  • Tamamlayıcı nesneler, kapsayıcı sınıfın statik oluşturucusundan başlatılır (yani nesne oluşturulduğunda oluşturulur).
  • Normal nesneler, söz konusu nesneye ilk kez geç eriştiklerinde, yani ilk kez kullanıldıklarında başlatılır.

Daha fazla yöntem vardır ancak şimdilik bilmeniz gereken tek şey, sınıflardaki sabit değerleri bir tamamlayıcı nesne içinde sarmalamaktır.

Bu görevde sınıfların davranışını genişletmeyi öğrenirsiniz. Bir sınıfın davranışını genişletmek için yardımcı program işlevleri yazmak çok yaygın bir durumdur. Kotlin, şu yardımcı işlevleri tanımlamak için kullanışlı bir söz dizimi sağlar: uzantı işlevleri.

Uzantı işlevleri, kaynak koduna erişmek zorunda kalmadan mevcut bir sınıfa işlev eklemenize olanak tanır. Örneğin, bunları paketinizin bir parçası olan Extensions.kt dosyasında bildirebilirsiniz. Bu işlem sınıfı gerçekten değiştirmez, ancak işlevi ilgili sınıfın nesneleri üzerinde çağırırken nokta gösterimini kullanmanıza olanak tanır.

1. Adım: Uzantı işlevi yazın

  1. Hâlâ REPL üzerinde çalışıyor, bir dizenin boşluk içerip içermediğini kontrol etmek için basit bir uzantı işlevi yazabilirsiniz: hasSpaces(). İşlev adının üzerinde, üzerinde çalıştığı sınıfla birlikte bulunur. İşlevin içinde, this çağrıldığı nesneyi ve it find() çağrısındaki iteratörü ifade eder.
fun String.hasSpaces(): Boolean {
    val found = this.find { it == ' ' }
    return found != null
}
println("Does it have spaces?".hasSpaces())
⇒ true
  1. hasSpaces() işlevini basitleştirebilirsiniz. this açıkça gerekli değildir ve işlev, tek bir ifadeye indirilip bu ifadeye döndürülebilir. Bu nedenle, {} etrafındaki kıvrık ayraçlara da gerek yoktur.
fun String.hasSpaces() = find { it == ' ' } != null

2. Adım: Uzantıların sınırlamalarını öğrenin

Uzantı işlevleri yalnızca yalnızca genişlettikleri sınıfın herkese açık API'sine erişebilir. private değişkenlerine erişilemiyor.

  1. private işaretli bir mülke uzantı işlevleri eklemeyi deneyin.
class AquariumPlant(val color: String, private val size: Int)

fun AquariumPlant.isRed() = color == "red"    // OK
fun AquariumPlant.isBig() = size > 50         // gives error
⇒ error: cannot access 'size': it is private in 'AquariumPlant'
  1. Aşağıdaki kodu inceleyin ve ne yazdıracağını öğrenin.
open class AquariumPlant(val color: String, private val size: Int)

class GreenLeafyPlant(size: Int) : AquariumPlant("green", size)

fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")

val plant = GreenLeafyPlant(size = 10)
plant.print()
println("\n")
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print()  // what will it print?
⇒ GreenLeafyPlant
AquariumPlant

plant.print() baskılar GreenLeafyPlant. aquariumPlant.print() değeri GreenLeafyPlant olarak atandığından aquariumPlant.print() öğesinin de GreenLeafyPlant yazdırmasını bekleyebilirsiniz. Ancak tür, derleme zamanında çözüldüğünden AquariumPlant yazdırılır.

3. Adım: Uzantı özelliği ekleyin

Kotlin, uzantı işlevlerine ek olarak, uzantı özellikleri de eklemenize olanak tanır. Uzantı işlevlerinde olduğu gibi, genişlettiğiniz sınıfı ve ardından bir nokta, ardından özellik adını belirtirsiniz.

  1. Hâlâ REPL üzerinde çalışıyorsa isGreen AquariumPlant uzantısına bir renk ekleyin. Bu renk yeşilse true.
val AquariumPlant.isGreen: Boolean
   get() = color == "green"

isGreen özelliğine normal bir mülk gibi erişilebilir. Bu değere erişildiğinde, değeri almak için isGreen alanının alıcısı çağrılır.

  1. aquariumPlant değişkeni için isGreen özelliğini yazdırın ve sonucu gözlemleyin.
aquariumPlant.isGreen
⇒ res4: kotlin.Boolean = true

4. Adım: Boş değerli alıcılar hakkında bilgi edinin

Uzattığınız sınıfa alıcı adı verilir ve bu sınıfı boş hale getirmek mümkündür. Bunu yaparsanız gövdede kullanılan this değişkeni null olabilir. Bu nedenle, bunu test ettiğinizden emin olun. Null'a karşılık gelen bir alıcı almak istersiniz. Arayanlar, null değişkenlerde uzantı yönteminizi çağırmak istiyorsa veya fonksiyonunuz null işlevine uygulandığında varsayılan bir davranış istiyorsanız.

  1. Hâlâ REPL üzerinde çalışıyor, boş bir alıcı alan bir pull() yöntemi tanımlayın. Bu bilgi, türden önce ve noktadan sonra soru işareti ? ile belirtilir. Gövdenin içinde, Questionmark-dot-application ?.apply. özelliğini kullanarak this öğesinin null olup olmadığını test edebilirsiniz.
fun AquariumPlant?.pull() {
   this?.apply {
       println("removing $this")
   }
}

val plant: AquariumPlant? = null
plant.pull()
  1. Bu durumda, programı çalıştırdığınızda herhangi bir çıkış olmaz. plant null olduğundan, iç println() çağrılmaz.

Uzantı işlevleri çok güçlüdür ve Kotlin standart kitaplığının çoğu uzantı işlevleri olarak uygulanır.

Bu derste; koleksiyonlar hakkında daha fazla bilgi edindiniz, sabit değerleri öğrendiniz ve uzantı işlevlerinin ve özelliklerinin gücünü keşfettiniz.

  • Bir işlevden birden fazla değer döndürmek için çiftler ve üçlüler kullanılabilir. Örneğin:
    val twoLists = fish.partition { isFreshWater(it) }
  • Kotlin, List için reversed(), contains() ve subList() gibi birçok faydalı işleve sahiptir.
  • Anahtarlar değerlerle eşlemek için HashMap kullanılabilir. Örneğin:
    val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
  • const anahtar kelimesini kullanarak derleme zamanı sabitlerini bildirin. Bunları en üst düzeye yerleştirebilir, tek bir nesne halinde düzenleyebilir veya bir tamamlayıcı nesneye yerleştirebilirsiniz.
  • Tamamlayıcı nesne, companion anahtar kelimesiyle tanımlanan bir sınıf tanımındaki tekli nesnedir.
  • Uzantı işlevleri ve özellikleri bir sınıfa işlev ekleyebilir. Örneğin:
    fun String.hasSpaces() = find { it == ' ' } != null
  • null alıcı, bir sınıfta null olabilecek uzantılar oluşturmanıza olanak sağlar. Kod yürütülmeden önce null kontrolü yapmak için ?. operatörü apply ile eşlenebilir. Örneğin:
    this?.apply { println("removing $this") }

Kotlin belgeleri

Bu kurstaki herhangi bir konu hakkında daha fazla bilgi edinmek istiyorsanız veya takılırsanız en iyi başlangıç noktanız olarak https://kotlinlang.org adresine gidin.

Kotlin eğiticileri

https://try.kotlinlang.org web sitesinde web tabanlı çevirmen olarak Kotlin Koans adlı zengin eğitici videolar ve örnekler içeren eksiksiz bir referans dokümanı grubu bulunur.

Udacity kursu

Bu konudaki Udacity kursunu görüntülemek için Programcılar için Kotlin Eğitim Programı'na bakın.

IntelliJ IDEA

IntelliJ IDEA ile ilgili belgeler JetBrains web sitesinde bulunabilir.

Bu bölümde, bir eğitmen tarafından sunulan kurs kapsamında bu codelab üzerinden çalışan öğrenciler için olası ev ödevi ödevleri listelenmektedir. Öğretmenin şunları yapması gerekir:

  • Gerekirse ev ödevini atayın.
  • Öğrencilere ev ödevlerinin nasıl gönderileceğini bildirin.
  • Ev ödevlerine not verin.

Öğretmenler bu önerileri istedikleri kadar kullanabilir veya uygun görebilir ve uygun olan diğer ev ödevlerini atayabilirler.

Bu codelab'de kendiniz çalışıyorsanız, bilginizi test etmek için bu ödevlerden yararlanabilirsiniz.

Bu soruları yanıtlayın

1. Soru

Aşağıdakilerden hangisi bir listenin kopyasını döndürür?

makbuz add()

makbuz remove()

makbuz reversed()

makbuz contains()

2. Soru

class AquariumPlant(val color: String, val size: Int, private val cost: Double, val leafy: Boolean) üzerindeki bu uzantı işlevlerinden hangisi derleyici hatası verir?

makbuz fun AquariumPlant.isRed() = color == "red"

makbuz fun AquariumPlant.isBig() = size > 45

makbuz fun AquariumPlant.isExpensive() = cost > 10.00

makbuz fun AquariumPlant.isNotLeafy() = leafy == false

3. Soru

Aşağıdakilerden hangisi const val ile sabit değerleri tanımlayabileceğiniz bir yer değildir?

▢,

normal derslerde ▢

Singleton nesnelerinde ▢

Companion nesnesinde ▢

Sonraki derse geçin: 5.2 Generics

Diğer codelab'lerin bağlantıları da dahil olmak üzere kursa genel bir bakış için Programcılar için Kotlin Eğitim Programı: Kursa hoş geldiniz başlıklı makaleyi inceleyin.