İçeriğe geç
Educora
İleri24 dk7 / 14

Sınıflar, veri sınıfları, kalıtım ve arayüzler

Verileri ve davranışları bir araya getir: sınıflar, yapıcılar, veri sınıfları, `object` ve eşlik eden nesneler, `open` ile kalıtım ve arayüzler.

Kendini test et
Bu derste öğreneceklerin
  • Birincil yapıcısı, özellikleri ve metotları olan bir sınıf yazmak
  • Veri sınıfının sağladığı toString, == ve copy olanaklarını kullanmak
  • object ve eşlik eden nesnenin ne işe yaradığını açıklamak
  • open/override ile kalıtım kurmak ve bir arayüzü uygulamak

Bir e-okul defteri yazıyorsun: her öğrencinin adı, sınıfı ve notları var; yeni not eklemen ve ortalamayı hesaplaman gerekiyor. 30 öğrenci için ayrı ayrı değişkenler kısa sürede karmaşaya dönüşür. Nesne yönelimli programlama başka bir yol sunar: verileri ve onlarla çalışan fonksiyonları tek bir yerde, yani bir nesnede birleştirmek. Kotlin bunu çok kısa bir yazımla yapar.

Sınıf, yapıcı ve özellikler

Sınıf nesnelerin planıdır, nesne ise bu plana göre oluşturulmuş somut bir örnektir. Kotlin'de birincil yapıcı (primary constructor), sınıf adından hemen sonra parantez içinde yazılır: orada val ya da var ile tanımlanan parametreler doğrudan sınıfın özellikleri olur. Sınıfın içindeki fonksiyonlara metot denir. Nesne new sözcüğü olmadan, bir fonksiyon çağrısı gibi oluşturulur: Student("Aysel", 8).

Kotlin
class Student(val name: String, var grade: Int) {
    val scores = mutableListOf<Int>()

    fun addScore(score: Int) {
        scores.add(score)
    }

    fun average() = scores.average()

    fun introduce() = "I am $name, grade $grade"
}

fun main() {
    val aysel = Student("Aysel", 8)
    aysel.addScore(5)
    aysel.addScore(4)
    println(aysel.introduce())
    println(aysel.scores)
    println(aysel.average())
    aysel.grade = 9
    println(aysel.introduce())
}
Beklenen çıktı
I am Aysel, grade 8
[5, 4]
4.5
I am Aysel, grade 9
grade bir var olduğu için değiştirilebilir; name ise val'dır ve değişmez.

Veri sınıfları

Sıradan bir sınıfın nesnesini yazdırırsan Student@1b6d3586 gibi anlaşılmaz bir metin görürsün; aynı verilere sahip iki nesne de == ile eşit sayılmaz. Çoğu zaman bir sınıf yalnızca veri tutmak içindir: kullanıcı, ürün, sipariş. Böyle durumlarda sınıfın önüne data yazılır ve Kotlin otomatik olarak okunaklı bir toString, içeriği karşılaştıran bir ==, copy ve bileşenlere ayırma olanağı oluşturur.

Kotlin
data class User(val name: String, val age: Int)

fun main() {
    val u1 = User("Aysel", 15)
    val u2 = User("Aysel", 15)
    println(u1)
    println(u1 == u2)
    val older = u1.copy(age = 16)
    println(older)
    val (name, age) = older
    println("$name is $age")
}
Beklenen çıktı
User(name=Aysel, age=15)
true
User(name=Aysel, age=16)
Aysel is 16

object ve eşlik eden nesneler

Bazen programda bir şeyin yalnızca tek bir örneği olmalıdır: uygulama ayarları ya da veritabanı bağlantısı gibi. Bunun için class yerine object yazılır; bu hem sınıfı tanımlar hem de onun tek nesnesini oluşturur. Sınıfın içindeki companion object (eşlik eden nesne) ise nesne oluşturmadan doğrudan sınıf adıyla çağrılan fonksiyonlar ve sabitler içindir; örneğin nesne üreten “fabrika” fonksiyonları.

Kotlin
object Settings {
    var language = "az"
    fun describe() = "Language: $language"
}

class Temperature(val celsius: Double) {
    companion object {
        const val WATER_BOILS = 100.0
        fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
    }
}

fun main() {
    println(Settings.describe())
    Settings.language = "en"
    println(Settings.describe())
    val t = Temperature.fromFahrenheit(212.0)
    println(t.celsius)
    println(t.celsius == Temperature.WATER_BOILS)
}
Beklenen çıktı
Language: az
Language: en
100.0
true

Kalıtım: open ve override

Kalıtım, var olan bir sınıfın üzerine yeni bir sınıf kurmayı sağlar: alt sınıf, üst sınıfın özelliklerini ve metotlarını alır. Kotlin'de sınıflar varsayılan olarak kapalıdır; bir sınıftan türetebilmek için üst sınıfın open ile işaretlenmesi gerekir. Alt sınıfın değiştirebileceği metot da open olmalıdır; alt sınıf onun yeni sürümünü override ile yazar. Üst sınıf iki noktadan sonra belirtilir: class Dog(name: String) : Animal(name).

Kotlin
open class Animal(val name: String) {
    open fun sound() = "..."
    fun introduce() = "$name says ${sound()}"
}

class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof!"
}

class Cat(name: String) : Animal(name) {
    override fun sound() = "Meow!"
}

fun main() {
    val pets = listOf(Dog("Rex"), Cat("Mestan"), Animal("Nemo"))
    for (pet in pets) {
        println(pet.introduce())
    }
}
Beklenen çıktı
Rex says Woof!
Mestan says Meow!
Nemo says ...
Aynı introduce(), her nesnede o nesnenin kendi sound() sürümünü çağırır; buna çok biçimlilik (polimorfizm) denir.

Arayüzler

Arayüz (interface), sınıfların neler yapabilmesi gerektiğini tanımlayan bir sözleşmedir: özelliklerini ve metotlarını uygulayan her sınıf o arayüzün türüne ait olur. Arayüz bir metodun hazır bir sürümünü de içerebilir. Bir sınıf yalnızca bir sınıftan türeyebilir ama istediği sayıda arayüzü uygulayabilir.

Kotlin
interface Shape {
    val name: String
    fun area(): Double
    fun describe() = "$name: area ${area()}"
}

class Rectangle(val width: Double, val height: Double) : Shape {
    override val name = "Rectangle"
    override fun area() = width * height
}

class Circle(val radius: Double) : Shape {
    override val name = "Circle"
    override fun area() = 3.14 * radius * radius
}

fun main() {
    val shapes: List<Shape> = listOf(Rectangle(3.0, 4.0), Circle(2.0))
    shapes.forEach { println(it.describe()) }
    println(shapes.maxBy { it.area() }.name)
}
Beklenen çıktı
Rectangle: area 12.0
Circle: area 12.56
Circle
Kolaylık için π ≈ 3,14 alındı; tam değer kotlin.math.PI'dadır.
TanımNe zaman kullanılır
classverisi ve davranışı olan sıradan nesneler
data classçoğunlukla veri tutan nesneler
objecttek bir örneği olan şey
open classalt sınıflar için bir temel
interfacebirkaç sınıfın ortak yeteneği (sözleşme)

Önemli noktalar

  • class Student(val name: String): birincil yapıcı özelliği doğrudan tanımlar; nesne new olmadan oluşturulur.
  • data class otomatik olarak toString, içeriğe göre ==, copy ve bileşenlere ayırma kazanır.
  • object tek bir örnek oluşturur; companion object ise sınıf adıyla çağrılan üyeleri tutar.
  • Sınıflar ve metotlar varsayılan olarak kapalıdır: kalıtım için open, geçersiz kılmak için override gerekir.
  • Bir sınıf tek bir üst sınıftan türer ama birden çok arayüzü uygulayabilir.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
data class Point(val x: Int, val y: Int) için println(Point(1, 2)) ne yazdırır?