lateinit / lazy: Kotlin’de geç başlatmanın özü ve mekanizmaları

Yazar: IT Sectr Yayınlanma: 2026-06-23 Okuma süresi: 8 dk

Geç başlatma (lazy initialization), Kotlin’de bir nesne özelliğinin oluşturulma anında değil, ilk erişimde başlatıldığı bir mekanizmadır. JetBrains, 2024’e göre, lateinit ve lazy bu stratejiyi uygulamak için iki yerleşik araçtır. Her ikisi de geç başlatma sorununu çözer, ancak çalışma mekanizması ve uygulama kapsamı açısından temelde farklılık gösterir.

Anahtar Noktalar

  • lateinit — var özellikleri için bir değiştirici, nesne oluşturulduktan sonra başlatmaya izin verir
  • lazy — val özellikleri için bir temsilci, ilk erişimde değeri başlatır
  • lateinit var gerektirir ve JVM ilkel türlerini desteklemez
  • lazy varsayılan olarak iş parçacığı güvenlidir ve hesaplanan sonucu önbelleğe alır
  • lateinit erken erişimde UninitializedPropertyAccessException fırlatır

Kotlin’de geç başlatma nedir?

Geç başlatma, bir sınıf özelliğinin değerini nesne oluşturma anında değil, daha sonra ihtiyaç duyulduğunda aldığı bir desendir. Kotlin’de bu desen iki temelde farklı şekilde uygulanır: lateinit değiştiricisi ve lazy temsilcisi.

Her iki mekanizma da ortak bir sorunu çözer — bir özellik sınıfta var olmalıdır, ancak değeri ya nesne oluşturma anında bilinmemektedir ya da hesaplaması gereksiz yere yapmak için çok kaynak yoğundur. Google I/O 2023’e göre, tipik bir Android uygulamasındaki özelliklerin %40’a kadarı geç başlatma yoluyla optimize edilebilir ve başlangıç süresini %15–25 oranında azaltır.

lateinit ve lazy arasındaki seçim üç faktör tarafından belirlenir: özelliğin değişebilirliği (var veya val), yaşam döngüsü (tek veya çoklu atama) ve iş parçacığı güvenliği gereksinimleri (tek iş parçacıklı veya çok iş parçacıklı erişim).

Geç başlatma ne zaman kullanılır

İlk ve en yaygın senaryo Bağımlılık Enjeksiyonudur. Framework (Dagger, Hilt, Koin) bağımlılıkları nesne oluşturulduktan sonra enjekte eder, bu nedenle özellik kurucuda başlatılamaz. lateinit olmadan, tüm bağımlılıklar nullable olarak bildirilmeli ve her kullanımda kontrol edilmelidir.

İkinci senaryo ağır kaynaklardır: veritabanları, ağ istemcileri, dosya yöneticileri. Bunların oluşturulması zaman ve bellek gerektirir, bu nedenle yalnızca gerçekten kullanıldıklarında başlatılmalıdırlar. lazy bu gibi durumlar için idealdir ve tek seferlik oluşturmayı garanti eder.

Üçüncü durum, yaşam döngüsü işletim sistemi tarafından yönetilen Android bileşenleridir (Activity, Fragment, ViewModel). onCreate, onViewCreated veya ViewModel’in init bloğuna bağlı olan özellikler kurucuda başlatılamaz.

lateinit: mekanizma ve sınırlamalar

lateinit, Kotlin derleyicisinin başlatmayı ertelemesine izin veren var özellikleri için bir değiştiricidir. Derleyici kurucuda değer ataması gerektirmez, ancak her erişimde bir çalışma zamanı kontrolü oluşturur: özellik başlatılmamışsa, UninitializedPropertyAccessException fırlatır.

kotlin
class MainActivity {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

lateinit sınırlamaları: özellik var (val değil), boş olmayan ve ilkel türde (Int, Double, Boolean vb.) olmayan olarak bildirilmelidir. Nedeni, ilkel türlerin JVM ilkellerine derlenmesi ve “başlatılmamış” durumunun olmamasıdır. Boş değer alabilen özellikler için geç başlatma gerekli değildir: null zaten değerin olmadığı anlamına gelir.

lateinit özelliğinin durumunu kontrol etmek için, :: operatörü aracılığıyla yerleşik referansı kullanın: ::propertyName.isInitialized. Bu, bir istisna riski olmadan bir özelliğin başlatılıp başlatılmadığını kontrol etmenin tek güvenli yoludur. Kontrol yalnızca aynı sınıftan veya iç sınıftan kullanılabilir, harici koddan kullanılamaz.

kotlin
class LoginFragment {
    lateinit var binding: FragmentLoginBinding

    fun isReady(): Boolean {
        return ::binding.isInitialized
    }
}

lateinit performansı

lateinit başlatmadan sonra ek yük getirmez: değer atandıktan sonra, özellik erişimi doğrudan alan erişimiyle aynıdır. Tek maliyet, atamadan önce her okumada başlatma kontrolüdür. Başlatmadan sonra, JIT derleyicisi kontrolü optimize eder.

Önemli bir not: lateinit özellikleri satır içi sınıflarda kullanılamaz ve özel getter/setter’a sahip özellikler için desteklenmez. Bir özellik hesaplanmış erişim gerektiriyorsa, lateinit yerine lazy kullanın.

lazy: mekanizma ve avantajlar

lazy, Kotlin standart kütüphanesine yerleşik bir özellik temsilcisidir. Özelliğe ilk erişimde değeri hesaplar ve sonraki tüm çağrılar için sonucu önbelleğe alır. lateinit’in aksine, lazy yalnızca val ile çalışır ve başlatmadan sonra özelliği değişmez hale getirir.

kotlin
class UserRepository {
    private val database: Database by lazy {
        Database.create("users.db")
    }

    fun getUser(id: String): User {
        return database.query("SELECT * FROM users WHERE id = ?", id)
    }
}

lazy, iş parçacığı güvenliği mekanizmasını kontrol eden isteğe bağlı bir LazyThreadSafetyMode parametresi alır. Varsayılan SYNCHRONIZED’dır — kilitlemeli çift kontrol, birden çok iş parçacığından eşzamanlı erişimde bile tek başlatmayı garanti eder.

lazy iş parçacığı güvenliği modları

PUBLICATION modu paralel başlatmaya izin verir: birden çok iş parçacığı başlatma bloğunu aynı anda yürütebilir, ancak sonuç yalnızca ilk tamamlayan iş parçacığından kabul edilir. Yüksek rekabet durumunda SYNCHRONIZED’dan daha hızlıdır, ancak kaynak tüketimini artırır.

NONE modu senkronizasyonu tamamen devre dışı bırakır. Yalnızca erişimi tek bir iş parçacığından garanti edilen özellikler için kullanın. Bu modda lazy minimum ek yük ile çalışır — neredeyse doğrudan atama gibi.

kotlin
val heavyConfig: Config by lazy(LazyThreadSafetyMode.NONE) {
    Config.loadFromFile("config.json")
}

lazy ne zaman tercih edilir

lazy bir kez başlatılan bağımlılıklar için doğru seçimdir: depolar, ağ istemcileri, önbellekler, veritabanları. val semantiği yanlışlıkla üzerine yazmaya karşı korur ve varsayılan iş parçacığı güvenliği, kodu çok iş parçacıklı ortamlarda güvenli hale getirir. lazy ayrıca ilkel türlerle de doğru şekilde çalışır, bu lateinit ile imkansızdır.

Android’de, lazy genellikle by viewModels() aracılığıyla ViewModel bağımlılıklarını başlatmak veya Retrofit istemcileri oluşturmak için kullanılır. Ancak dikkatli olun: bir lazy bloğu bir Activity veya Fragment’e referans yakalarsa, bellek sızıntısına neden olabilir, çünkü temsilci, özelliğin ömrü boyunca kapanımı tutar.

lateinit vs lazy: yaklaşımların karşılaştırması

lateinit ve lazy arasındaki seçim bir tercih meselesi değil, özelliğin doğası tarafından belirlenen mimari bir karardır. Her mekanizma kendi görevini çözer ve uygulama alanları yalnızca kısmen örtüşür.

Kriterlateinitlazy
Özellik türüyalnızca varyalnızca val
Nullableizin verilmezizin verilir
İlkel türlerizin verilmezizin verilir
İş parçacığı güvenliğigaranti edilmezvarsayılan olarak SYNCHRONIZED
Durum kontrolü::x.isInitializedgerekmez
Hata durumunda istisnaUninitializedPropertyAccessExceptioninit bloğunda hata
Önbelleklemeuygulanmaztek hesaplama
Android BindingView Binding, Data Bindingkullanılmaz
DI frameworkleriDagger, Hilt, Koinmanuel enjeksiyon

lateinit’i, bir özelliğin başlatmadan sonra değişmesi gerektiğinde veya oluşturulması harici kod tarafından yönetildiğinde kullanın. Tipik bir örnek, Android Activity’de View Binding’dir: binding onCreate’te oluşturulur ancak var olarak kalır çünkü framework bu senaryo için val’i desteklemez.

lazy’yi, bir özellik bir kez başlatıldığında, hesaplaması pahalı olduğunda ve değer nesnenin ömrü boyunca değişmediğinde kullanın. Klasik bir örnek, depoya ilk erişimde bir Retrofit istemcisinin veya Room veritabanının tembel oluşturulmasıdır.

lateinit ve lazy’nin birleştirilmesi

Her iki mekanizma da aynı sınıf içinde aynı anda kullanılabilir. Örneğin, View Binding için lateinit ve bir depo için lazy. Bu, farklı özellikler için farklı gereksinimleri yansıtan normal bir uygulamadır. Önemli olan semantiği karıştırmamaktır: val gereken yerde lateinit kullanmayın ve yeniden atanması gereken özellikler için lazy kullanmayın.

lateinit ve lazy kullanırken yaygın hatalar

lateinit ile en yaygın hata, özelliğe başlatılmadan önce erişmektir. Bu, UninitializedPropertyAccessException’a yol açar ve derleme zamanında yakalanmaz çünkü Kotlin, geliştiricinin doğru başlatma sırasını sağlayacağına güvenir. Çözüm, belirsiz durumlarda erişimden önce ::property.isInitialized aracılığıyla her zaman durumu kontrol etmektir.

İkinci yaygın sorun, anlamsal olarak val olan özellikler için lateinit kullanmaktır. Değer bir kez ayarlanıp hiç değişmiyorsa, lazy daha doğru seçimdir. Özelliği değişmez yapar, yanlışlıkla üzerine yazmayı önler ve ücretsiz olarak iş parçacığı güvenliği ekler.

Üçüncü hata, yan etkileri olan lazydir. lazy başlatma bloğu, harici durumu değiştirmemeli veya diğer lazy özelliklerinin başlatma sırasına bağlı olmamalıdır, çünkü hesaplama sırası ilk erişime bağlıdır ve açık olmayabilir. lazy özellikleri birbirine referans verirse, bu döngüsel bağımlılığa ve StackOverflowError’a yol açar.

Dördüncü sorun, Android’de lazy yoluyla bellek sızıntılarıdır. Bir lazy bloğu bir Activity veya Fragment’e referans yakalarsa, temsilci kapanımı tutar ve çöp toplayıcı, bileşeni yok edildikten sonra bile serbest bırakamaz. Çözüm, lazy’yi yalnızca kısa ömürlü nesnelerle kullanmak veya Activity yerine Application bağlamını iletmektir.

Beşinci tipik hata, ilkel türlere lateinit uygulamaya çalışmaktır. Kotlin derleyicisi bunu sözdizimi düzeyinde engeller, ancak geliştiriciler nullable sarmalayıcılar aracılığıyla sınırlamayı aşmaya çalışır. Bu, gereksiz null kontrollerine yol açar ve geç başlatmanın faydalarını tamamen ortadan kaldırır.

Sıkça Sorulan Sorular

Kotlin’de lateinit ve lazy arasındaki fark nedir?

lateinit, var özellikleri için bir değiştiricidir ve kurucudan sonra başlatmaya izin verir. lazy, val özellikleri için bir temsilcidir, ilk erişimde değeri hesaplar ve önbelleğe alır. lateinit ilkel türleri ve nullable’ı desteklemezken, lazy varsayılan olarak iş parçacığı güvenlidir.

Bir lateinit özelliğinin başlatılıp başlatılmadığını kontrol edebilir miyim?

Evet, yerleşik özellik referansı aracılığıyla: ::propertyName.isInitialized. Metod, özellik zaten başlatılmışsa true döndürür. lateinit alanlarıyla çalışırken UninitializedPropertyAccessException’ı önlemenin tek güvenli yolu budur.

lateinit neden ilkel türlerle kullanılamaz?

İlkel türler — Int, Double, Boolean ve diğerleri — JVM ilkellerine (int, double, boolean) derlenir ve “başlatılmamış” durumları yoktur. lateinit null‑ı bir işaret olarak kullanır ve ilkeller null olamaz, bu nedenle mekanizma bu türler için fiziksel olarak imkansızdır.

lazy’nin varsayılan iş parçacığı güvenliği modu nedir?

Varsayılan LazyThreadSafetyMode.SYNCHRONIZED’dır — kilitlemeli çift kontrol, birden çok iş parçacığından eşzamanlı erişim altında tek başlatmayı garanti eder. Tek iş parçacıklı senaryolar için NONE, yüksek rekabet için PUBLICATION kullanın.

Android’de lateinit yerine lazy ne zaman kullanılmalıdır?

Özellik başlatmadan sonra değişmesi gerektiğinde veya oluşturulması framework tarafından yönetildiğinde. Tipik bir örnek, Android Activity’deki View Binding’dir: binding onCreate’te oluşturulur ve var olmalıdır. Bir kez başlatılan val bağımlılıkları için lazy kullanın.

Özet

  • lateinit — Kotlin var özellikleri için bir değiştirici, nullable olmadan kurucudan sonra başlatmaya izin verir
  • lazy — val için bir özellik temsilcisi, tek hesaplama ve otomatik sonuç önbellekleme
  • lateinit başlatmadan önce erişimde UninitializedPropertyAccessException fırlatır
  • lazy LazyThreadSafetyMode.SYNCHRONIZED aracılığıyla varsayılan olarak iş parçacığı güvenlidir
  • lateinit ilkel türler ve nullable özelliklerle uyumsuzdur
  • lazy bir kapanımda bağlam yakaladığında Android’de bellek sızıntısına neden olabilir
  • Değişebilir özellikler için lateinit’i, bir kez başlatılan val bağımlılıkları için lazy’yi seçin

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun