e
sv

Kotlin'de tembel ve lateinit değişkenlerini başlatma

avatar

Yazılım Method

  • e 0

    Mutlu

  • e 0

    Eğlenmiş

  • e 0

    Şaşırmış

  • e 0

    Kızgın

  • e 0

    Üzgün

Kotlin genellikle özellikleri tanımladığımız anda başlatmamızı ister. Özellikle yaşam döngüsü odaklı Android özellikleri söz konusu olduğunda, ideal başlangıç değerini bilmediğimizde bunu yapmak garip görünüyor.

Neyse ki, bu sorunu aşmanın bir yolu var. IntelliJ IDEA düzenleyicisi, bir sınıf özelliğini başlatmadan bildirirseniz sizi uyarır ve bir lateinit anahtar sözcüğü eklemenizi önerir.

Başlatılan bir özellik veya nesne programda gerçekten kullanılmazsa ne olur? Nesne oluşturma ağır bir süreç olduğundan, bu kullanılmayan başlatmalar programa yükümlülükler getirecektir. Bu, lateinit kurtarmaya gelebileceği bir başka örnek.

Bu makale, lateinit değiştiricisinin ve tembel temsilcinin kullanılmayan veya gereksiz erken başlatmalarla nasıl ilgilenebileceğini açıklayacaktır. Bu, Kotlin geliştirme iş akışınızı tamamen daha verimli hale getirecektir.

lateinit

lateinit anahtar sözcüğü, "geç başlatma" anlamına gelir. Bir sınıf özelliğiyle kullanıldığında, lateinit değiştiricisi, özelliğin, sınıfının nesne inşası sırasında başlatılmasını engeller.

Bellek, lateinit değişkenlerine, bildirildikleri zaman değil, yalnızca programda daha sonra başlatıldıklarında tahsis edilir. Bu, başlatmada esneklik açısından çok uygundur.

lateinit sunduğu bazı önemli özelliklere bakalım!

Ana Özellikler

İlk olarak, bellek, bildirim sırasında bir lateinit özelliğine tahsis edilmez. Başlatma, uygun gördüğünüzde daha sonra gerçekleşir.

Bir lateinit özelliği, program boyunca birden fazla kez değişebilir ve değiştirilebilir olması gerekir. Bu yüzden onu her zaman var olarak ilan etmelisiniz, val veya const olarak değil.

lateinit başlatma, özellikleri null yapılabilir türler olarak başlatırken ihtiyaç duyabileceğiniz tekrarlayan boş denetimlerden sizi kurtarabilir. lateinit özelliklerinin bu özelliği, null yapılabilir türü desteklemez.

Son noktamı genişleterek, lateinit , ilkel olmayan veri türleriyle iyi bir şekilde kullanılabilir. long veya int gibi ilkel türlerle çalışmaz. Bunun nedeni, bir lateinit özelliğine erişildiğinde, Kotlin'in özelliğin henüz başlatılmadığını belirtmek için kaputun altında boş bir değer sağlamasıdır.

İlkel türler null , bu nedenle başlatılmamış bir özelliği belirtmenin bir yolu yoktur. Sonuç olarak, ilkel türler, lateinit anahtar sözcüğüyle kullanıldığında bir istisna atar.

Son olarak, bir lateinit özelliği, erişilmeden önce bir noktada başlatılmalıdır, aksi takdirde aşağıda görüldüğü gibi bir UninitializedPropertyAccessException hatası verir:

Başlatılmamış Özellik Erişimi İstisna Hatası

Başlatmadan önce erişilen bir lateinit özelliği bu özel duruma yol açar.

Kotlin, bir lateinit özelliğinin başlatılıp başlatılmadığını kontrol etmenizi sağlar. Bu, az önce tartıştığımız başlatmayı kaldırma istisnasıyla başa çıkmak için kullanışlı olabilir.

 lateinit var myLateInitVar: String
...

if(::myLateInitVar.isInitialized) {
  // Bir şey yap
}

Kullanılan lateinit değiştirici örnekleri

Basit bir örnekle lateinit değiştiricisini çalışırken görelim. Aşağıdaki kod bir sınıfı tanımlar ve bazı özelliklerini kukla ve boş değerlerle başlatır.

 sınıf TwoRandomFruits {
  var meyve1: String = "domates" 
  var meyve2: String? = boş


  eğlenceli rastgeleMyFruits() {
      meyve1 = rastgeleMeyveler()
      meyve2 = muhtemelenNullRandomFruits()
  }


  eğlenceli randomFruits(): String { ... }
  eğlenceli muhtemelenNullRandomFruits(): Dize? { ... }
}

eğlence ana() {
    val rf= Rastgele Meyveler()
    rf.randomizeMyFruits()


    println(rf.fruit1.capitalize())
    println(rf.fruit2?.capitalize()) // Null-check
}

Bu, bir değişkeni başlatmanın en iyi yolu değildir, ancak bu durumda yine de işi yapar.

Yukarıda görebileceğiniz gibi, özelliği null yapılabilir yapmayı seçerseniz, onu her değiştirdiğinizde veya kullandığınızda null kontrol etmeniz gerekir. Bu oldukça sıkıcı ve sinir bozucu olabilir.

Bu sorunu lateinit değiştiricisi ile çözelim:

 sınıf TwoRandomFruits {
  lateinit var meyve1: String // İlk kukla değere gerek yok
  lateinit var meyve2: String // Null yapılabilir tür burada desteklenmez


  eğlenceli rastgeleMyFruits() {
      meyve1 = rastgeleMeyveler()
      meyve2 = ne zaman {
          muhtemelenNullRandomFruits() == null -> "Domates" // Boş değerleri işleme
          else -> muhtemelenNullRandomFruits()!!
      }
  }


  eğlenceli randomFruits(): String { ... }
  eğlenceli muhtemelenNullRandomFruits(): Dize? { ... }
}

eğlence ana() {
    val rf= Rastgele Meyveler()
    rf.randomizeMyFruits()


    println(rf.fruit1.capitalize())
    println(rf.fruit2.capitalize())
}

Bu kodu burada çalışırken görebilirsiniz .

lateinit uygulaması kendisi için konuşur ve değişkenlerle başa çıkmanın düzgün bir yolunu gösterir! lateinit varsayılan davranışı dışında, buradaki ana paket, null yapılabilir türü kullanmaktan ne kadar kolay kaçınabileceğimizdir.

Yaşam döngüsü odaklı özellikler ve lateinit

Veri bağlama, daha sonra bir etkinliği başlatmak için lateinit kullanmanın başka bir örneğidir . Geliştiriciler, farklı görünümlere erişmek için diğer yöntemlerde referans olarak kullanmak için genellikle bağlama değişkenini daha önce başlatmak ister.

Aşağıdaki MainActivity sınıfında, aynı şeyi elde etmek için lateinit değiştiricisi ile bağlamayı ilan ettik.

 paket com.test.lateinit

androidx.appcompat.app.AppCompatActivity'yi içe aktar
içe aktarmak ...

sınıf MainActivity : AppCompatActivity() {
  lateinit var bağlama: ActivityMainBinding

  eğlenceyi geçersiz kıl onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    bağlama = DataBindingUtil.setContentView(bu, R.layout.activity_main)

    ...
  }
  ...
}

MainActivity için bağlama, yalnızca etkinlik yaşam döngüsü işlevi onCreate() tetiklendiğinde başlatılabilir. Bu nedenle, bağlamayı lateinit değiştiricisi ile bildirmek burada tam bir anlam ifade eder.

lateinit zaman kullanılır

Normal değişken başlatma ile, bir kukla ve büyük olasılıkla boş bir değer eklemeniz gerekir. Bu, erişildiklerinde çok sayıda boş denetim ekleyecektir.

 // Geleneksel başlatma
var name: String? = boş
...    
isim = getDataFromSomeAPI()
...
// 'name'ye her erişildiğinde bir boş kontrol gerekli olacaktır.
isim?.let { it-> 
  println(it.büyük harf())
}

// Lateinit başlatma
lateinit var name: String
...
isim = getDatafromSomeAPI()
...
println(isim.büyük harf())

Özellikle bir özelliğin sık sık dalgalanması muhtemel olduğunda, bu tekrarlanan boş denetimlerden kaçınmak için lateinit değiştiricisini kullanabiliriz.

lateinit kullanırken hatırlanması gerekenler

Bir lateinit özelliğine erişmeden önce her zaman başlatmayı hatırlamak iyidir, aksi takdirde derleme sırasında atılan büyük bir istisna görürsünüz.

Ayrıca bir var bildirimi kullanarak özelliği değiştirilebilir tuttuğunuzdan emin olun. val ve const kullanmak, lateinit çalışmayacağı değişmez özellikleri gösterdikleri için hiçbir anlam ifade etmeyecektir.

Son olarak, verilen özelliğin veri türü ilkel olduğunda veya boş değer olasılığı yüksek olduğunda lateinit kullanmaktan kaçının. Bu durumlar için yapılmamıştır ve ilkel veya null yapılabilir türleri desteklemez.

Kotlin'de tembel delegasyon

Adından da anlaşılacağı gibi, Kotlin'deki lazy , bir özelliği tembel bir şekilde başlatır. Esasen, bir referans oluşturur, ancak yalnızca özellik ilk kez kullanıldığında veya çağrıldığında başlatma için gider.

Şimdi, bunun normal başlatmadan nasıl farklı olduğunu soruyor olabilirsiniz. Eh, bir sınıf nesnesi inşası sırasında, tüm genel ve özel özellikleri, yapıcısı içinde başlatılır. Bir sınıfta değişkenlerin başlatılmasıyla ilgili bazı ek yükler vardır; ne kadar fazla değişken olursa, ek yük o kadar büyük olur.

Bir örnekle anlayalım:

 sınıf X {
  eğlenceli doThis() {}
}

şık {
  val mustIdoThis: Boolean = SomeAPI.guide()
  değer x = X()

  if(shouldIdoThis) {
    x.doThis()
  }
  ...
}

Kullanmamasına rağmen, yukarıdaki koddaki Y sınıfı hala X sınıfından oluşturulmuş bir nesneye sahiptir. X Sınıfı, eğer yoğun bir şekilde inşa edilmiş bir sınıfsa, Y de yavaşlatacaktır.

Gereksiz nesne oluşturma verimsizdir ve mevcut sınıfı yavaşlatabilir. Program akışına bağlı olarak, belirli koşullar altında bazı özellikler veya nesneler gerekli olmayabilir.

Ayrıca, özellikler veya nesneler, oluşturmak için diğer özelliklere veya nesnelere bağlı olabilir. Tembel delegasyon bu iki olasılığı verimli bir şekilde ele alır.

Ana Özellikler

Tembel başlatmaya sahip bir değişken, çağrılana veya kullanılana kadar başlatılmayacaktır. Bu şekilde, değişken yalnızca bir kez başlatılır ve daha sonra programda daha fazla kullanım için değeri önbelleğe alınır.

Tembel temsilci ile başlatılan bir özelliğin baştan sona aynı değeri kullanması gerektiği için, doğası gereği değişmezdir ve genellikle salt okunur özellikler için kullanılır. Bir val beyanı ile işaretlemeniz gerekir.

İş parçacığı için güvenlidir, yani yalnızca bir kez hesaplanır ve varsayılan olarak tüm iş parçacıkları tarafından paylaşılır. Başlatıldığında, program boyunca başlatılan değeri hatırlar veya önbelleğe alır.

lateinit aksine tembel temsilci, değeri okurken ve yazarken ara işlemleri gerçekleştirmesine izin veren özel bir ayarlayıcı ve alıcıyı destekler.

Kullanımdaki tembel yetkilendirme örneği

Aşağıdaki kod, belirli şekillerin alanlarını hesaplamak için basit matematik uygular. Bir daire durumunda, hesaplama, pi için sabit bir değer gerektirir.

 sınıf Alanı {
  pi değeri: Kayan nokta = 3.14f


  eğlenceli daire(yarıçap: Int): Kayan nokta = pi * yarıçap * yarıçap
  eğlenceli dikdörtgen(uzunluk: Int, genişlik: Int = uzunluk): Int = uzunluk * genişlik
  eğlenceli üçgen(taban: Int, yükseklik: Int): Kayan nokta = taban * yükseklik * .5f
}

eğlence ana() {
  val alanı = Alan()
  val squareSideLength = 51


  println("Dikdörtgenimizin alanı ${area.rectangle(squareSideLength)}")
}

Yukarıda da görebileceğiniz gibi, herhangi bir dairenin alanı hesaplanmadı, bu da pi tanımımızı işe yaramaz hale getiriyor. pi özelliği hala başlatılır ve bellek tahsis edilir.

Tembel heyet ile bu sorunu düzeltelim:

 sınıf Alanı {
  val pi: tembel {
    3.14f
  } 


  eğlence çemberi(...) = ...
  eğlenceli dikdörtgen(...) = ...
  eğlenceli üçgen(...) = ...
}

eğlence ana() {
  val alanı = Alan()
  val squareSideLength = 51
  val daireYarıçap = 37

  println("Dikdörtgenimizin alanı ${area.rectangle(squareSideLength)}")
  println("Çemberimizin alanı ${area.circle(circleRadius)}")
}

Yukarıdaki örneğin bir demosunu burada görebilirsiniz .

Tembel delegasyonun yukarıdaki uygulaması, yalnızca erişildiğinde pi kullanır. Erişildikten sonra değeri önbelleğe alınır ve program boyunca kullanılmak üzere ayrılır. Bunu sonraki örneklerde nesnelerle çalışırken göreceğiz.

Ara işlemler

Tembel temsilci aracılığıyla değerler yazarken bazı ara eylemleri nasıl ekleyebileceğiniz aşağıda açıklanmıştır. Aşağıdaki kod lazy , bir Android etkinliğinde bir TextView başlatır.

Bu TextView , MainActivity içinde ilk kez çağrıldığında, temsilcinin lambda işlevinde aşağıda gösterildiği gibi, LazyInit etiketine sahip bir hata ayıklama mesajı günlüğe kaydedilir:

 ...
sınıf MainActivity : AppCompatActivity() {
  eğlenceyi geçersiz kıl onCreate(...) {
    ...  
    val sampleTextView: tembel { tarafından TextView
      Log.d("LazyInit", "sampleTextView")
      findViewById(R.id.sampleTextView)
    }
  }
  ...
}

Android uygulamalarında tembel yetkilendirme

Şimdi Android uygulamalarında tembel yetkilendirme uygulamasına geçelim. En basit kullanım durumu, bir görünümü koşullu olarak kullanan ve yöneten bir Android etkinliğine ilişkin önceki örneğimiz olabilir.

 paket com.test.lazy

androidx.appcompat.app.AppCompatActivity'yi içe aktar
içe aktarmak ...

sınıf MainActivity : AppCompatActivity() {
  lateinit var bağlama: ActivityMainBinding

  eğlenceyi geçersiz kıl onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    bağlama = DataBindingUtil.setContentView(bu, R.layout.activity_main)


    tembel { tarafından val sharedPrefs
      aktivite?
        .getPreferences(Context.MODE_PRIVATE)
    } 


    val startButton tembel {
      bağlama.startDüğmesi
    }


    if(sharedPrefs.getBoolean("firstUse", true)) {
      startButton.isVisible = doğru
      startButton.setOnClickListener {
        // Yerleştirmeyi bitirin, ana ekrana geçin; bunun gibi bir şey
        sharedPrefs.setBoolean("ilkKullanım", yanlış)
      }
    }

  }
}

Yukarıda, SharedPreferences ve bir Button tembel temsilci ile başlattık. Mantık, paylaşılan tercihlerden alınan bir boole değerine dayalı bir işe alım ekranının uygulanmasını gerektirir.

by lazy ve = lazy arasındaki fark

by lazy ifadesi, tembel temsilci tarafından doğrudan belirli bir özelliğe bir geliştirme ekler. Başlatma, ilk erişimden sonra yalnızca bir kez gerçekleşir.

 tembel tarafından val prop {
  ...
}

Öte yandan, = lazy ifadesi, bunun yerine isInitialized() yetkilendirme yöntemini kullanabileceğiniz veya buna value özelliği ile erişebileceğiniz delege nesnesine bir başvuru içerir.

 val prop = tembel {
  ...
}
...

if(prop.isInitialized()) {
  println(prop.değer)
}

Yukarıdaki kodun hızlı bir demosunu burada görebilirsiniz.

lazy ne zaman kullanılır

Diğer sınıf nesnelerinin çoklu ve/veya koşullu yaratımlarını içeren bir sınıfı hafifletmek için tembel temsilciler kullanmayı düşünün. Nesne oluşturma, sınıfın dahili bir özelliğine bağlıysa, tembel delegasyon gidilecek yoldur.

 sınıf Çalışanı {
    ...
    fun showDetails(id: Int): List<Any> {
        val çalışanKayıtlar tembel {
            EmployeeRecords(id) // Nesnenin dahili bir özelliğe bağımlılığı
        }
    }
    ...
}

lazy kullanırken hatırlanması gerekenler

Tembel başlatma, bir şeyi yalnızca bir kez ve yalnızca çağrıldığında başlatan bir yetkilendirmedir. Gereksiz nesne oluşturmayı önlemek içindir.

Temsilci nesnesi, ilk erişimde döndürülen değeri önbelleğe alır. Bu önbelleğe alınan değer, gerektiğinde programda daha fazla kullanılır.

Değerleri okurken ve yazarken ara işlemler için özel alıcı ve ayarlayıcısından yararlanabilirsiniz. Ayrıca, program boyunca değişmeden kalan değerlerle en iyi şekilde çalıştığını düşündüğüm için değişmez türlerle kullanmayı tercih ediyorum.

Çözüm

Bu yazıda lateinit değiştiricisini ve tembel delegasyonunu tartıştık. Kullanımlarını gösteren bazı temel örnekler gösterdik ve ayrıca Android geliştirmedeki bazı pratik kullanım örneklerinden bahsettik.

Bu başlangıç kitabını sonuna kadar okumak için zaman ayırdığınız için teşekkür ederiz! Uygulama geliştirme yolculuğunuzda bu iki özelliği uygulamak için bu kılavuzu kullanabileceğinizi umuyorum.

Kotlin'deki Tembellik ve <code>lateinit</code> değişkenlerini başlatma yazısı ilk olarak LogRocket Blog'da göründü.

etiketlerETİKETLER
Üzgünüm, bu içerik için hiç etiket bulunmuyor.

Sıradaki içerik:

Kotlin'de tembel ve lateinit değişkenlerini başlatma