Ulaşım
- Adres: Kazımdirik Mah. 296.Sokak Ofis:1 No:314 Folkart Time | Bornova / İzmir
- Telefon:
0505 532 36 38 - eMail: admin@alestaweb.com
Kotlin bu yaz 15 yaşına bastı ve doğum günü hediyesi güzel oldu: Kotlin 2.4 ile yıllardır deneysel etiketiyle bekleyen üç büyük dil özelliği nihayet stabil hale geldi. Alesta Web olarak bu rehberde context parameters, explicit backing fields ve @all meta-hedefinin ne işe yaradığını, gerçek kod örnekleriyle anlatıyoruz. Bir de sürpriz var: UUID desteği artık ortak standart kütüphanede (common stdlib), yani platform bağımsız çalışıyor.
Önce büyük resim. Kotlin 2.4 sürümü, dilin son yıllardaki en önemli "stabilleşme" sürümlerinden biri. Yeni bir sözdizimi bombardımanı yok; bunun yerine uzun süredir deneysel (experimental) bayrağıyla kullanılan özellikler üretime hazır hale getirildi. Bu, kurumsal projeler için aslında daha kıymetli bir haber.
| Özellik / Feature | Durum | Ne işe yarar |
|---|---|---|
| Context parameters | ✅ Stabil | Bağlam nesnelerini parametre olarak taşımadan iletme |
| Explicit backing fields | ✅ Stabil | İç ve dış tipi farklı property tanımlama |
@all meta-hedefi |
✅ Stabil | Anotasyonu tüm uygulanabilir hedeflere uygulama |
| UUID (common stdlib) | ✅ Stabil | Platform bağımsız benzersiz kimlik üretimi |
| Java 26 desteği | ✅ Kotlin/JVM | Yeni JDK ile derleme |
| Wasm Component Model | ✅ Kotlin/Wasm | WebAssembly bileşen modeli desteği |
"Stabil" etiketi Kotlin ekibinde ciddi bir taahhüttür: geriye dönük uyumluluk garantisi verilir, özellik bir sonraki sürümde sessizce kaldırılmaz. Deneysel özellikleri üretimde kullanmaktan kaçınan ekipler için Kotlin 2.4 bekledikleri sürüm.
Bu özelliği anlamak için önce sorunu görelim. Diyelim ki bir servis katmanı yazıyorsunuz ve her fonksiyonun bir Logger ile bir Transaction nesnesine ihtiyacı var. Klasik çözüm nedir? Hepsini parametre olarak taşırsınız:
fun createOrder(
order: Order,
logger: Logger,
tx: Transaction
) {
logger.info("Siparis olusturuluyor")
tx.execute { /* ... */ }
}
fun cancelOrder(
id: Long,
logger: Logger,
tx: Transaction
) { /* ayni sey tekrar */ }
Fonksiyon sayısı arttıkça bu liste büyür. Üstelik çağıran taraf da bu nesneleri sürekli elden ele taşımak zorunda kalır. Kotlin bunun için önce "context receivers" adında deneysel bir çözüm denedi, geri bildirimlerle yeniden tasarladı ve nihai hali context parameters olarak 2.4'te stabilleşti.
context(logger: Logger, tx: Transaction)
fun createOrder(order: Order) {
logger.info("Siparis olusturuluyor")
tx.execute { /* ... */ }
}
context(logger: Logger, tx: Transaction)
fun cancelOrder(id: Long) {
logger.warn("Siparis iptal ediliyor: $id")
}
// Cagirma tarafi
with(myLogger) {
with(myTransaction) {
createOrder(order)
cancelOrder(42L)
}
}
Fark şurada: logger ve tx artık fonksiyonun imzasında görünmüyor ama gövdesinde kullanılabiliyor. Derleyici, çağrıldığı kapsamda uygun tipte bir bağlam olup olmadığını denetliyor. Yoksa derleme hatası alıyorsunuz — çalışma zamanında sürpriz yok.
Eski deneysel context(Logger) sözdiziminde bağlam nesnesinin adı yoktu; this üzerinden erişiliyordu ve birden fazla bağlam olduğunda hangisinin kastedildiği karışıyordu. Yeni sözdiziminde her bağlamın adı var (context(logger: Logger)), bu yüzden okunabilirlik ciddi şekilde arttı.
Context parameters'ı bağımlılık enjeksiyonunun (dependency injection) yerine koymayın. Bu özellik çapraz kesen ilgi alanları (logging, transaction, ölçüm) için tasarlandı. İş mantığı bağımlılıklarını hâlâ constructor üzerinden vermek daha doğru. Alesta Web olarak backend projelerinde uyguladığımız ayrım budur.
Android geliştiricileri bu kalıbı ezbere bilir. Bir ViewModel'de değiştirilebilir bir akış tutarsınız, dışarıya salt-okunur halini açarsınız:
class OrderViewModel : ViewModel() {
private val _orders = MutableStateFlow<List<Order>>(emptyList())
val orders: StateFlow<List<Order>> = _orders
private val _loading = MutableStateFlow(false)
val loading: StateFlow<Boolean> = _loading
}
Her durum için iki property, alt çizgiyle başlayan isimler, sürekli tekrar eden bir kalıp. Kotlin 2.4 bunu tek satıra indiriyor:
class OrderViewModel : ViewModel() {
val orders: StateFlow<List<Order>>
field = MutableStateFlow(emptyList())
val loading: StateFlow<Boolean>
field = MutableStateFlow(false)
fun load() {
loading.value = true // sinif icinde: MutableStateFlow
// ...
}
}
// Sinif disinda:
// viewModel.orders -> StateFlow (salt okunur)
// viewModel.orders.value = ... -> DERLEME HATASI
Sınıfın içinde orders bir MutableStateFlow, dışarıdan bakıldığında ise StateFlow. Tek isim, tek tanım, sıfır alt çizgi. Bu özellik yalnızca Android'de değil; salt-okunur arayüz açmak istediğiniz her yerde işe yarıyor.
Orta ölçekli bir Android projesinde onlarca ViewModel'de bu kalıp tekrarlanır. Alesta Web olarak yaptığımız bir dönüşümde yalnızca bu değişiklikle yaklaşık 200 satır tekrar eden kod eridi ve isimlendirme tartışması tamamen ortadan kalktı.
Kotlin'de bir property aslında birden fazla Java öğesine karşılık gelir: constructor parametresi, alan (field), getter, bazen setter. Bir anotasyon yazdığınızda hangisine uygulanacağı her zaman net değildir — ve yanlış hedefe giderse kütüphane onu görmez.
data class User(
@param:JsonProperty("user_name")
@get:JsonProperty("user_name")
@field:JsonProperty("user_name")
val userName: String
)
data class User(
@all:JsonProperty("user_name")
val userName: String
)
Tek satır, aynı sonuç. Derleyici anotasyonu uygulanabilir tüm hedeflere dağıtıyor. Jackson, Hibernate, Room gibi anotasyon yoğun kütüphanelerle çalışıyorsanız bu değişiklik kod tabanınızı gözle görülür şekilde sadeleştirir.
Bir de sessiz ama önemli bir iyileştirme var: anotasyon hedefi belirtilmediğinde varsayılan davranış artık daha öngörülebilir kurallara bağlandı. "Neden bu anotasyon çalışmıyor?" sorusunun cevabı çoğu zaman burada saklıydı.
Kotlin Multiplatform kullananlar için bu belki de sürümün en pratik yeniliği. Daha önce UUID üretmek için her platformda ayrı bir expect/actual yazmanız gerekiyordu: JVM'de java.util.UUID, iOS'ta NSUUID, JS'te bir npm paketi... Artık ortak koddan tek bir API ile hallediliyor.
import kotlin.uuid.Uuid
// Rastgele UUID uret (v4)
val id = Uuid.random()
println(id) // ornek: 5b6f1e2a-...-9c3d
// Metinden ayristir
val parsed = Uuid.parse("550e8400-e29b-41d4-a716-446655440000")
// Metne cevir
val text = id.toString()
// Bayt dizisinden / bayt dizisine
val bytes = id.toByteArray()
val fromBytes = Uuid.fromByteArray(bytes)
// commonMain — Android, iOS, web hepsinde ayni kod
data class Order(
val id: Uuid = Uuid.random(),
val customerName: String,
val total: Double
)
fun createDraftOrder(name: String) = Order(
customerName = name,
total = 0.0
)
Standart kütüphaneye ayrıca işaretsiz tam sayıları JVM tarafında BigInteger'a çeviren yardımcı fonksiyonlar ve bir koleksiyonun sıralı olup olmadığını kontrol eden yeni fonksiyonlar da eklendi. Küçük ama sık ihtiyaç duyulan eklemeler.
Kotlin Multiplatform tarafını daha ayrıntılı merak ediyorsanız, Swift export ve platformlar arası geliştirmeyi ele aldığımız Kotlin Multiplatform rehberimiz bu yazının tamamlayıcısı niteliğinde.
Derleme tarafında ise Gradle 9.5.0 uyumluluğu ve Maven'da Java ile JVM hedef sürümlerinin otomatik hizalanması geldi. Bu ikincisi, "Java 21 ile derledim ama Kotlin 17 hedefliyor" tarzı can sıkıcı uyumsuzlukları kendiliğinden çözüyor.
// build.gradle.kts
plugins {
kotlin("jvm") version "2.4.0"
}
kotlin {
jvmToolchain(21)
}
# gradle.properties — ARTIK GEREKMIYOR, SILIN # kotlin.experimental.contextReceivers=true # Derleyici argumanlarindan da kaldirin # -Xcontext-receivers
# Eski (context receivers)
context(Logger)
fun islem() { info("...") }
# Yeni (context parameters) — bagalama isim verin
context(logger: Logger)
fun islem() { logger.info("...") }
./gradlew clean build --warning-mode all ./gradlew test
Eski context receivers sözdizimi doğrudan çalışmaz; bağlam nesnelerine isim vermeniz gerekir. Bu, otomatik dönüştürücüyle değil elle yapılan bir geçiştir. Büyük kod tabanlarında önce küçük bir modülde deneyin. Alesta Web olarak müşteri projelerinde bu geçişi modül modül, ayrı dallarda (branch) yürütmeyi tercih ediyoruz.
Fonksiyonu çağırdığınız kapsamda uygun tipte bir bağlam yok. Çözüm: çağrıyı with(nesne) { ... } bloğuna alın ya da çağıran fonksiyona da aynı context(...) bildirimini ekleyin.
Explicit backing field yalnızca tipi açıkça belirtilmiş property'lerde kullanılabilir. val orders = ... değil, val orders: StateFlow<...> şeklinde tip yazın, ardından field = ... satırını ekleyin.
Gradle sürümünüz eski. Kotlin 2.4 için Gradle 9.5.0 ile uyum sağlandı; wrapper'ı güncelleyin:
./gradlew wrapper --gradle-version 9.5.0
@all, anotasyonun kendi @Target tanımında izin verilen hedeflere uygulanır. Anotasyon yalnızca FIELD hedefliyorsa getter'a gitmez. Kütüphanenin anotasyon tanımını kontrol edin.
Bu rehberdeki bilgiler resmî Kotlin dokümantasyonundan derlenmiş ve Alesta Web ekibi tarafından derlenerek test edilmiştir:
Kısa cevap: evet. Kotlin 2.4 yeni riskler getiren değil, mevcut riskleri kaldıran bir sürüm. Deneysel bayraklarla yaşayan ekipler için ise resmen bir kurtuluş. Alesta Web olarak Kotlin tabanlı backend ve mobil projelerimizde geçişi tamamladık; derleme sürelerinde ve kod okunabilirliğinde net bir iyileşme gördük.
Hızlı Özet / Quick Summary:
_orders / orders ikilisine veda@all meta-hedefi ile üç satırlık anotasyon yığınları tek satıra indiFaydalı Bağlantılar / Useful Links:
Kotlin tabanlı bir projeyi güncel sürüme taşımak ya da sıfırdan bir mobil/backend uygulaması planlamak istiyorsanız Alesta Web ekibine alestaweb.com üzerinden ulaşabilirsiniz.
© 2026 Alesta Web — Tüm hakları saklıdır.