Kotlin — Null Safety

Ekrem Hatipoglu
5 min readMar 17, 2018

--

Bu yazıda Kotlin’de bulunan Null Safety özelliğini inceleyeceğiz.Öncelikle null kelimesinin ne demek olduğunu öğrenelim.

null — Türkçeye boş,hiç anlamında çevrilebilir. Bilgisayar dilinde değişkenin veya nesnenin bellekte bulunamaması olayıdır.Bu olay sonucu NullPointerException hatası alırız.

NullPointerException önemli bir hatadır çünkü önceden görmek çok zordur, uygulamanın çalışma zamanında ortaya çıkar ve uygulamanın çökmesine neden olur. Kotlin bu tip durumları engellemek için Null Safety özelliğini dile eklemiştir.

Kotlin dilinde 2 tane referans tipi vardır. Bunlar Nullable and Non-Nullable yani null olabilen ve null olamayan olarak türkçeye çevrilebilir.

Kotlinde default’ta değişkenler null olamaz. Her değişken tanımladığımızda onu initialize etmekle yani bir başlangıç değeri vermekle yükümlüyüz.

var greeting: String = "Hello, World"
greeting = null // Derleme hatası

Değişkenlerin null olabilmesine izin vermek istiyorsak değişken tipini belirttikten sonra ? işaretini koymalıyız.

var nullableGreeting: String? = "Hello, World"
nullableGreeting = null // Hata vermez

NullPointerException hatasını boş bir değişkene veya fonksiyona erişmeye çalışırken aldığımızı söylemiştik. Kotlin null olabilen değişkenlere veya fonksiyonlara direk erişimi engeller böylelikle bir çok NullPointerException hatasını engellemiş olur.

Örneğin yukarıdaki ilk kod bloğunda tanımladığımız greeting değişkeni asla null değer alamaz bu yüzden Kotlin bu değişkenin properties’lerine direk erişime izin verir.

val len = greeting.length //Herhangi bir hata alınmaz
val upper = greeting.toUpperCase()

Ancak ikinci kod bloğunda tanımladığımız nullableGreeting değişkeni ? kullanıldığından null değer atanmasına izin verilmiştir.Bu değişkenin properties’lerine değişkenin değeri null mı diye kontrol etmeksizin direk erişimimiz olanaksızdır.

val len = nullableGreeting.length // Derleyici HATASI
val upper = nullableGreeting.toUpperCase() // Derleyici HATASI

Nullable Types

Kotlin’de null olabilen değişkenleri kontrol etmenin birden çok yolu vardır.

1. if ile null kontrolü

Java’da sıklıkla kullandığımız gibi değişkene erişmeden önce bir if kontrolü yaparak değişkenin değeri null mı diye kontrol edebiliriz.

val nullableName: String? = "Ekrem"

if(nullableName != null) {
println("Merhaba, ${nullableName.toUpperCase()}.")
println("Adınızın karakter uzunluğu : ${nullableName.length}")
} else {
println("Merhaba")
}

if ile bir null kontrolü yapıldığında derleyici bunu hatırlar ver değişkenin properties’lerine erişime izin verir.

2. Safe call operator: ?.

İf ile null kontrolü yapılışı çok kolay ancak büyük projelerde çok uzun ve gereksiz kod kalabalığıdır. Bu durumdan kurtulmak için Kotlin bizlere safe call operatörünü?. önerir. Safe call operatörü null kontrolü işlemini tek satırda yapmamıza olanak sağlar.

nullableName?.toUpperCase()

Eğer null bir String ifadenin uzunluğunu veya içeriğini büyük harfle safely bir şekilde ekrana basmak istersek aşağıdaki gibi bir kod bloğu yazarız.

val nullableName: String? = null

println(nullableName?.toUpperCase())
println(nullableName?.length)
// Ekrana aşağıdaki değerleri yazar. Herhangi bir hata vermez
null
null

Burada nullableName String ifadesinin null değerde olamasına rağmen ifadenin karakter uzunluğunu ekrana yazdırmak istediğimizde herhangi bir NullPointerException hatası almayız.

But what if you don’t want to print anything if the variable is null?

Eğer değişkenin değeri null ise ve biz bunu ekrana yazdırmak istemiyorsak safe call operatörünü let ifadesi ile birlikte kullanmalıyız.

val nullableName: String? = null

nullableName?.let { println(it.toUpperCase()) }
nullableName?.let { println(it.length) }

// Ekrana hiçbir şey yazmaz

Yukarıdaki kod sadece değişken değeri null değilse çalışır.

Bunların dışında Safe call operatörü ile tek satırda birden fazla null kontrolüde yapabilirsiniz. Örneğin

val currentCity: String? = user?.address?.city

currentCity değişkeni user, address veya city değişkenlerinden biri null olursa null değeri alıcaktır.( Bu işlemi birde if’lerle yaptığınızı düşünün !)

3. Elvis operator: ?:

Elvis operaörü atanmak istenen değer null olduğunda onun yerine default bir değer atmak için kullanılır.

val name = nullableName ?: "Guest"

Yukarıdaki kod bloğu aynı zaman aşağıdaki gibide yazılabilir.

val name = if(nullableName != null) nullableName else "Guest"

Gördüğünüz gibi Elvis operatörü iki değer alır eğer ilk değer null değilse ilkini null ise de aldığı ikinci değeri geriye döndürür.(return) Aşağıdaki kod bloklarındaki gibi komleks işlemlerde yapılabilir.

val currentCity = user?.address?.city ?: "Unknown"
val len = nullableName?.length ?: -1

Ayrıca Elvis operatöründe return ve throw anahatar kelimelerinede kullanabiliriz. Bu durum bir fonksiyonda önkoşulları kontrol ederken çok kullanışlı bir durumdur.throw örneği aşağıdaki gibidir,

val name = nullableName ?: throw IllegalArgumentException("Name can not be null")

4. Not null assertion : !! Operator

Bu operatör null olabilen bir değişkenin null kontrolü yapılmadan çağrılmasını sağlar. Hatalı kullanımda NullPointerException hatası almamız olasıdır. Bu operatörü kullanmaktan mümkün olduğunca kaçınınız.

val nullableName: String? = null
nullableName!!.toUpperCase() // Çalışma zamanında
//NullPointerException Hatası döndürür.

Null Safety ve Java Interoperability

Kotlin, Java dili ile %100 birlikte çalışabilir ancak Java Null Safety özelliğini desteklemez. Kotlin Java’da tanımlanan değişkenlerin null olup olamadığı hakkında hiçbir bilgiye sahip olamadığı için ?. operatörünü kullanmamız için bizi zorunlu tutmaz ancak yazdığımız kodlarda bu operaötürü kullanırsak NullPointerException hatası alma ihtimalimiz bir o kadar azalır.

Aşağıdaki gibi bir Java sınıfı oluşturduğumuzu varsayalım,

public class User {
private final String name;

public User(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

Burada Kotlin dili name değişkenin null olmasına izin verilip verilmediğini hiçbir şekilde bilemez bunun sebebinin Java’da Null Safety diye bir özelliğin olmaması olduğunu açıklamıştık.

val javaUser = User(null)

println(javaUser.name.toUpperCase()) // Allowed (Throws NullPointerException)
println(javaUser.name.length) // Allowed (Throws NullPointerException)

User sınıfından yukarıdaki gibi bir nesne ürettiğimizi varsayalım, Derleyici bu kodun çalışmasına izin verir ancak çalışma zamanında name değişkeni null olduğundan NullPointerException hatası alırız.

val javaUser = User(null)

println(javaUser.name?.toUpperCase()) // Allowed (Prints null)
println(javaUser.name?.length) // Allowed (Prints null)

Yukarıdaki kod bloğunda gördüğünüz gibi name değişkenini böylede çağırabiliriz. Bu yol daha güvenlidir. Çalışma zamanındada herhangi bir hatayla karşılaşmayız, ekrana null yazar. Ekrana null yazmasını istemiyorsak daha önce söylediğimiz gibi let ile de yazabiliriz.

Nullability Annotations

Java’nın Null Safety özelliğini desteklemediğini söylemiştik ancak javax.validation.constraints, org.jetbrains.annotations gibi paketler tarafından sağlanan değişkenleri null olabilir veya null olamaz gibi etiketlemek için @Nullable and @NotNull annotation’lar kullanırız.

Java derleyicisi bu annotation’ları kullanmaz ancak IDE’ler, ORM kütüphaneleri ve diğer araçlar null değerlerle çalışırken kod yazan kişiye yardımcı olmak amacıyla bu annotation’ları kullanır.

Nullability ve Collections

Kotlin dilinin Collection API’si, Java’nın Collection API’sinin üstüne inşa edilmiştir ama Kotlin dilinde Collection API nullability özelliğini tamamen destekler.

? işareti kullanılmayan değişkenler null değer alamadığından koleksiyonlarda null değer alamaz.Örneğin aşağıdaki gibi bir kullanım söz konusu olamaz.

val regularList: List<Int> = listOf(1, 2, null, 3) // Derleme HATASI
  1. Null Değer Alabilen Koleksiyonlar

Koleksiyonun alabileceği değişken türünü belirttikten ? işareti kullanırsak koleksiyon null değerlerde alabilir.

val listOfNullableTypes: List<Int?> = listOf(1, 2, null, 3) // Works

Null değer alabilen koleksiyonun içinden null değerleri atmak istersek filterNotNull() fonksiyonunu kullanabiliriz.

val notNullList: List<Int> = listOfNullableTypes.filterNotNull()

2. Null Olabilen Koleksiyonlar

Null değer alabilen koleksiyonlar ile Null olabilen koleksiyonlar arasında bir fark vardır.Null değer alabilen koleksiyonlar null olamazlar yani

var listOfNullableTypes: List<Int?> = listOf(1, 2, null, 3) // Works
listOfNullableTypes = null // Derleme Hatası

Bunun sebebi biz sadece koleksiyonun içine alabileceği değerlerin null olabilme iznini verdik.Bu yüzden koleksiyon null olamaz. Koleksiyonun null olabilmesi için aşağıdaki şekilde tanımlanması gerekmektedir.

var nullableList: List<Int>? = listOf(1, 2, 3)
nullableList = null // Works

3. Hem Null Olabilen Hem de Null Değer Alabilen Koleksiyonlar

Hem null olabilen hem de null değer alabilen koleksiyonlar aşağıdaki şekilde tanımlanır.

var nullableListOfNullableTypes: List<Int?>? = listOf(1, 2, null, 3) 
nullableListOfNullableTypes = null

Sonuç

Hepsi bu kadar. Bu yazıda, Kotlin’de Null Safety özelliğini ve NullPointerException hatasından nasıl korunucağınızı öğrendiniz.

Okuduğunuz için teşekkürler.

--

--