Teknik konular hakkında Türkçe yazmak gerçekten çok zor. Bir çok kelimenin Türkçe karşılığını bulmakta zorlanıyorum. Yarı Türkçe yarı İngilizce yazmak hiç hoşuma gitmese de aklımdakileri bir an önce yazıya döküp paylaşmak için şimdilik çok fazla kafaya takmadan yazıyorum. Kimse kusura bakmasın lütfen.

Bir önceki yazımda Kotlin’in statik ‘type’ sistemli ve güvenli bir programlama dili olduğunu söylemiştim. Bu yazıda, ne demek istediğimi daha detaylı bir şekilde açıklamaya çalışacağım.

kotlin.Any ve Sınıf Hiyerarşisi

Kotlin’de class hiyerarşinin en tepesinde Any vardır. Hiçbir sınıftan türemeyen (super class’ı olmayan) bir sınıf oluşturursanız bu sınıfın super class’ı otomatik olarak Any olur. Bire bir aynısı olmasa da Any‘yi java’daki Object sınıfı olarak düşünebiliriz.

Any

Şekildeki gibi bir sınıf hiyerarşisini aşağıdaki Kotlin sınıfları ile oluşturabiliriz.

open class View(var position: Vector3)

class ImageView(position: Vector3) : View(position) {
    fun setDrawable(drawable: Drawable) {}
}

class TextView(position: Vector3) : View(position) {
    fun setText(s: String) {}
}

ImageView ve TextView sınıflarını View sınıfından türetebilmek için View sınıfını open class olarak tanımlamamız gerekir çünkü Kotlin’de tüm sınıflar default olarak final‘dır. Aynı Java’da olduğu gibi Kotlin type sistemi de super-type/sub-type ilişkisini kontrol eder. Super-type bir değişkene sub-type bir değer atanabilir fakat bunun tersi mümkün değildir.

val view : View = ImageView(Vector3(1,1,1)) // ok
val anotherImageView : ImageView = view // ERROR

kotlin.Unit

Kotlin expression-oriented bir programlama dili oldugu için her statement bir değer döndürmek zorundadır. Örneğin if ve when birer expression’dır.

val max = if (a > b) a else b

val dayString = when(dayOfWeek) {
    1 -> "Pazartesi"
    2 -> "Sali"
    3 -> "Carsamba"
    4 -> "Persembe"
    5 -> "Cuma"
    6 -> "Cumartesi"
    7 -> "Pazar"
    else -> "N/A"
}

Kotlin bu yönüyle Java’dan ayrılır, functional programlama dillerine yakınlaşır. If statement’ların java’da bir değer döndürmediklerini zaten biliyoruz. Yukarıdaki Kotlin if statement örneğini Java’da yazmaya kalkarsak derleyici aşağıdaki gibi bir hata verecektir.

java if statement

Java’daki void ‘un karşılığı Kotlin’de Unit‘tir. Geriye birşey döndürme ihtiyacı olmayan fonksiyonların (sadece side effect’i olan fonksiyonlar) return tipi Unit olur.

fun printMessage(message: String?) : Unit {
    System.out.println(message)
}

Fonksiyonları tanımlarken Unit return tipini yazmak zorunda değiliz. Yukarıdaki printMessage fonksiyonunu Unit kullanmadan da yazabiliriz.

fun printMessage(message: String?) {
    System.out.println(message)
}

kotlin.Nothing

Kotlin standart library içerisinde Nothing diye de bir sınıf var. Bu özel sınıf tüm sınıf hiyerarşinin en altında yer alıyor. Eğer bir fonksiyon bir değer üretmiyorsa (sonsuz bir döngüyse ya da exception throw ediyorsa) return tipi olarak Nothing kullanılabilir. Bu sayede IDE’ler ya da derleyiciler Nothing dönen fonsiyonlardan sonra çalışacak kodlar için unreachable code uyarısı yapabilirler.

fun error() : Nothing = throw RuntimeException("Crashed!")

Kotlin’de Primitive Tipler Yok

Kotlin’de Java’daki int, long, float gibi primitive tipler yoktur. Herşey metodunu çağırabileceğimiz bir object’tir. Java’daki primitive tip olan int‘e karşılık Kotlin’de Int vardır ve Int super-type’ı Any olan bir sınıftır. Primitive tiplerin olmamasi memory kulanimi açısından problem olabilir diye endişelenmeye gerek yok çünkü Kotlin Compiler, Int (ve diğer built-in tipler Long, Float, Double, Byte …) için gerekli optimizasyonunu yapmaktadır. Örneğin aşağıdaki kotlin ve java kodları derlendiğinde aynı byte code oluşur.

//Kotlin
val x = 5 // compiler tipi otomatik anlar (type inference)
val y : Int = 4
val z = x + y

//Java
int x = 5;
int y = 4;
int z = x + y;

Elveda NullPointerException

Kotlin’in güvenli bir programalama dili olmasını sağlayan bir diğer özelliği de tiplerin “non null” ya da “nullable” olarak tanımlanabilmesidir. Örneğin Int ve Int? farklı tiplerdir fakat Int‘i Int?‘ın sub-type’ı olarak düşünebiliriz.

var a : Int = 5
var b : Int? = 10
a = b // *** (ERROR) non-null bir tip olan a'ya nullable bir tip atayamayız
b = a // (OK) nullable bir tipe non-null bir tip atayabiliriz.

Yukaridaki View-TextView-ImageView class hiyerarşisini nullable tipleri de hesaba katarsak aşağıdaki gibi çizebiliriz.

Any

Nullable ve non-null tipler, type system’e dahil olduğu için Kotlin derleyicisinin NULL güvenliğini (null safety) sağlamak için ekstra bir şey yapmasına gerek kalmıyor.

Production ortamında oluşan Exception’lar için bir istatistik hazırlamışlar ve bu istatistiğe göre en fazla karşılaşılan exception sizin de tahmin edebileceğiniz gibi meşhur(!) NullPointerException çıkmış. Bu bilgiye nasıl ulaştıklarını sorgulamadım çünkü yıllardır benim de karşıma hata olarak çoğunlukla NPE çıktı. NPE bir programcı hatası olduğu için Kotlin derleyicisini tasarlarken programcıların bu hataya tekrar tekrar düşmemeleri için nullable ve non null tipleri birbirinden ayırmışlar.

Nullable değişkenlerin metodlarına ve property’lerine güvenli bir şekilde (NPE’siz) erişmek için safe call operator?.’) kullanabiliriz.

val tv : TextView = TextView(Vector3(1,1,1))
val s1 = tv.getText()

val nullableTv : TextView? = null
//val s2 = nullableTv.getText() --> (ERROR) nullable textview'in metodunu doğrudan çağırmamıza derleyici izin vermez
val s2 = nullableTv?.getText()

Güvenli erişimler zincir halinde de kullanılabilir. Örneğin:

val tv : TextView? = TextView(Vector3(1,1,1))
val textLength = tv?.getText()?.length

Yukaridaki örnekte:

  • textLength değişkeninin “inferred” tipi Int?‘dir.
  • tv değişkeni null referans içeriyorsa getText() metodu çağırılmaz ve textLength null olur.
  • tv null değilse ve getText() metodu null dışında bir değer dönerse textLength‘e Int tipinde bir değer atanır.

textLength değişkeninin hiçbir şekilde null olmasını istemiyorsak Elvis Operator?:’ kullanabiliriz.

var nonNullTextLength = tv?.getText()?.length ?: 0

Yukarıdakı örnekte nonNullTextLength değişkeninin inferred tipi Int (non null)’tir. Elvis operatörü solundaki expression (tv?.getText()?.length) değeri null’dan farklıysa solundaki yoksa sağındaki expression değerini döner.

Peki Kotlin’de hiç mi NullPointerException olmaz? İstersek olur. NPE’siz yapamam diyenler için !! operatörü var. Aşağıdaki örnek çalıştırılırsa kotlin.KotlinNullPointerException oluşur.

val nullableTv : TextView? = null
val s = nullableTv!!.getText()

NPE ile karşılaşmanın başka yolları da var. Aşağıdaki npe() fonksiyonu çağırıldığında NullPointerException fırlatır.

fun npe() {
    throw NullPointerException("npe forever")
}

Ayrıca java dünyasından çağırdığımız fonksiyonların da NullPointerException fırlatma potansiyeli olduğunu unutmamak gerekir.