Kotlin Delegated Properties

Sertaç Okan Çelik
Delivery Hero Tech Hub
5 min readJan 16, 2022
Image from Unsplash

Propertyler sınıf genelinde kullanabileceğimiz değişkenleri ifade etmek için kullanılan terimlerdir. Tanımlanan bu değişkenlerin alabileceği değerler genellikle tanımlandıkları yerde verilir.

Property değişkenlerinin değer değişimlerini dinleme, değişkene bir kere değer ataması yapılmasını veya tanımlanan bu değişkene başka bir sınıf üzerinden değer ataması yapmak istediğimizde ne yapabiliriz? 🤔

Burada karşımıza Kotlin’in bize sağladığı kolaylıklardan biri olan Delegated Properties çıkıyor.

Bu yazımda delegation properties konusunu kod örnekleriyle anlatmaya çalışacağım. Biraz uzun bir yazı olabilir umarım faydalı olur :)

Programlamada delegation yapılmak istenen bir işin ilgili sınıf üzerinden değil başka bir sınıf üzerinden yapılmasını ifade eder.

ReadOnlyProperty

Tanımlanan değişkenin değerinin başka bir sınıf üzerinden getValue metoduyla sadece okunabilmesini sağlayan delegated property’dir.

val ile tanımlanır. var ile tanımlandığınızda hata alırsınız.

ReadOnlyProperty interface metodları
ReadOnlyProperty interface’i ile delegated property tanımlama

ReadOnlyProperty oluştururken interface kullanmak zorunda değilsiniz operator ve extension metodları ile ReadOnlyProperty tanımlanabilir.

operator getValue metodu ile delegated property tanımlama
Extension metot ile delegated property tanımlama

ReadWriteProperty

Tanımlanan değişkenin değerinin başka bir sınıf üzerinden getValue ve setValue metodlarıyla hem okunmasını hem de değerinin değiştirilmesini sağlayan delegated property’dir.

ReadWriteProperty interface’i ReadOnlyProperty interface’inden türetilir. getValue metodu ReadOnlyProperty interface’inden gelmektedir.
ReadWriteProperty interface’i ile delegated property tanımlama

ReadOnlyProperty gibi operator ve extension metodları ile ReadWriteProperty tanımlanabilir.

operator getValue ve setValue metodları ile delegated property tanımlama
Extension metodları ile delegated property tanımlama

Lazy

Lazy delegated property oluşturma metodları

Tanımlanmak istenen değişkene ilk defa değeri kullanılmak üzere erişildiğinde değer atamasının yapıldığı initializer kod bloğu bir kere çağrılır ve kod bloğunun döndüğü değer değişkene atanır ve daha sonra bu değişkenin değerinin kullanıldığı diğer yerlerde ilk defa initializer kod bloğunun döndüğü ve değişkene atanan değer kullanılır.

Peki nedir yukarıdaki cümledeki initializer kod bloğunun bir kere çalışması ve kod bloğundan dönen değerin değişkene atanması ve sonraki kullanımlarda bu değerin kullanılması? 🤔

Aşağıdaki kod örneği üzerinden anlatmaya çalışayım.

Yukarıdaki kod örneğinde LazyThreadSafetyMode.SYNCHRONIZED kullanılan lazy metodundaki mode parametresinin varsayılan değeridir.

Peki nedir LazyThreadSafetyMode ve SYNCHRONIZED dışında başka hangi değerleri alabilir? 🤔

LazyThreadSafetyMode değişkene ilk erişildiğinde değişkene değer atamasının yapıldığı initializer kod bloğunun aynı anda kaç tane thread tarafından çağrılabileceğini ve değişkene değer atamasının nasıl yapılabileceğini belirleyen enum sınıfıdır.

LazyThreadSafetyMode.SYNCHRONIZED dışında LazyThreadSafetyMode.PUBLICATION ve LazyThreadSafetyMode.NONE değerlerini alabilir.

Thread-safe kısmını alabileceği değerler üzerinden anlatmaya çalışayım.

  • LazyThreadSafetyMode.SYNCHRONIZED

Thread erişimi konusunda en katı moddur.

initializer kod bloğuna tek bir threadin erişebilmesine izin verir ve kod bloğundan dönen değerin değişkene atanmasını sağlar. Daha sonra değişkene erişmeye çalışan diğer threadler için initializer kod bloğu tekrar çağrılmaz ve değişkene tekrar değer ataması yapılmaz ve initializer kod bloğunun ilk çağrıldığındaki dönen değer kullanırlar.

initializer kod bloğuna tek bir threadin erişimini sağlamak amacıyla thread kontrolleri olduğu için değer ataması diğer modlara göre daha yavaştır.

  • LazyThreadSafetyMode.PUBLICATION

SYNCHRONIZED moduna göre thread erişiminde daha esnektir.

initializer kod bloğuna birden fazla threadin erişebilmesine ve kod bloğundan değer dönebilmesine izin verir. Değişkene ilk erişen thread’in kod bloğundan dönen değer değişkene atanır. Diğer thread erişimlerinde aynı değer kullanılır.

  • LazyThreadSafetyMode.NONE

Thread erişim konusunda en esnek moddur.

initializer kod bloğuna birden fazla threadin erişmesine izin verir. Birden fazla thread tarafından erişildiğinde değişkenin değeri kestirilemez. Bu yüzden initializer kod bloğuna tek bir thread tarafından erişildiğinden emin olduğunda kullanılmalıdır.

Değişkene birden fazla thread tarafından erişim olduğu durumlarda kullanımı tavsiye edilmez.

Thread erişimi ve initializer kod bloğunun değer kontrolü olmadığı için diğer modlara göre değişkenin değer ataması daha hızlı olur.

Lazy değişkenlerde initializer bloğunda hata alınırsa değişkene sonraki erişimde değer ataması yapılır.

Observable Delegated Properties

Değer değişimlerini dinleyebildiğimiz propertylerdir. Parametre olarak başlangıç değeri ve değer değişimlerinde çağrılan bir kod bloğu alırlar.

Observable ve vetoable olmak üzere iki çeşidi vardır.

Observable

Değişkenin aldığı eski ve yeni değere erişmemizi sağlar. Değişkene yeni değer atandıktan sonra onChange çağrılır yani değer ataması yapıldıktan sonra bu değişken bir yerde kullanıldığında yeni atanan değer üzerinden kullanılır.

Yukarıdaki cümleyi bir kod örneği ile açıklamaya çalışayım.

Vetoable

Değişkenin aldığı eski ve yeni değere erişmemizi sağlar. Observable’dan farklı olarak değişkene yeni değer atanmadan önce onChange çağrılır yani değer ataması yapıldıktan sonra bu değişken bir yerde kullanıldığında onChange kod bloğu true dönmediği sürece eski atanan değer, true dönerse yeni atana değer kullanılır. onChange kod bloğuna değişimin olabilmesi için gereken koşullar yazılabilir.

onChange kod bloğunu değişkenin değeri her değiştirilmeye çalışıldığı durumda çağrılır bu yüzden kod bloğunun içindeki kodlar her seferinde tekrar tekrar çalışır bundan kaçınmak için kod bloğunda ekstra kontroller yapmak gerekebilir.

Notnull

Kotlin Int, Boolean, Long, Float, Double gibi primitif değişkenler tanımladığınızda başlangıç değeri atamanızı bekler.

Peki primitif değişkenlerin değer atamasını tanımlanmalarından sonra yapmak istediğimizde ne yapabiliriz? 🤔

Akıllara gelebilecek ilk yol nullable değişken tanımlamak ama bu durumda bu değişkenin kullanıldığı her yerde null kontrolü yapmanız ya da Kotlin’in bize sağladığı şekilde “?.” kullanmanız gerekir.

Bir diğer yol olarak da değişkeni lateinit var ile tanımlamayı deneyebilirsiniz. Ama ufak bir sorun var. 😦

Kotlin lateinit var ile primitif değişken tanımlamanıza izin vermez.

Peki primitif değişkenleri lateinit var ile tanımlayamıyorsak; nullable yapmadan ya da başlangıç değeri atamadan nasıl tanımlayabiliriz? 🤔

Bu durumda karşımıza aşağıda örnek kullanımını gördüğünüz notnull delegated property çıkıyor. Primitif değişkenleri nullable yapmadan veya başlangıç değeri atamadan kullanmamızı sağlıyor.

Notnull delegation ile değişken tanımlarken dikkatli olmanız gerekmektedir. Değişkeni değer ataması yapmadan kullanmaya çalışırsanız IllegalStateException hatası alırsınız.

Map

Sınıf değişkenlerinin adlarını “key” olarak kullanıp, map içindeki “value” karşılığına Map ve MutableMap için tanımlanmış extension metodları üzerinden ulaşılmasını sağlayan delegated property’dir.

Map extension metodu
MutableMap extension metodları

Map kullanıldığında değişkenleri val kullanarak tanımlamanız gerekmektedir. var ile tanımladığınızda hata alırsınız. var ile tanımlamak istediğinizde MutableMap kullanmanız gerekir.

--

--