Alıntı

Scala 'lazy val'

Sahipleri : Yusuf Boyacıgil
Bu yazı izinle alıntılanmıştır : http://yboyacigil.com/turkish/scala/scala-lazy-val/
Scala’da değişken/argüman tanımlarken iki tane anahtar kelime var: var ve val
Scala’da değişken/argüman tanımlarken iki tane anahtar kelime var: var ve val. İlki için söylecek çok bir şey yok. Değişken tanımlamak için var’ı kullanmak durumundasın. Ama ikincisi aynı Java’daki final gibi sadece ilk seferinde bir değer atanmasını istediklerini tanımlamaya yarıyor. İkinci defa bir atama yapamıyorsun; aslında program içerisinde böyle sadece bir kere atama yapmaktan ziyade val ile tanımladıkların daha çok sonradan değerinin değiştirilmesini istemediğin değişkenler içindir.
scala> var x = { println("x=1"); 1 }
x=1
x: Int = 1
scala>  lazy val y = { println("y=2"); 2 }
y: Int = <lazy>
scala> y
y=2
res0: Int = 2
var ve val'ı sadece değişkenleri tanımlarken değil aynı zamanda fonkisyon ve sınıf yapılandırıcıları (constructor) parametrelerini tanımlarken de kullanabilirsin. Bu durumda var ile tanımladığın parametreler fonksiyon ve yapılandırıcı içerisinde değiştirilebilirken, val ile tanımladıklarının sadece değerini okuyabilirsin.
Bunların yanında bir lazy val diye bir anahtar kelime daha var. Lazy isminden anlaşılabileceği veya programlamaya aşina olanlar için belli şimdi değil sonrayı çağrıştırıyor. Değişkenin değerini şimdi değil de sonra ata demek. Sonra ne zaman? İlk defa o değişkene kod çalışırken erişildiği zaman.
scala> abstract class A { val x: String 
     | println(x.length)
     | }
defined class A
scala> object B extends A { lazy val x = "Yusuf" } 
defined module B
scala> B
x len: 5
res2: B.type = B$@6a48ffbc
Yukarıda y değişkeni lazy val olarak tanımladık ve erişene kadar değer atamadığını görebiliyoruz. Yukarıdaki örneklerde atama yaparken { println("..."); ... } dikkatinizi çekmiştir. Scala’da kod blokları {...} arasında tanımlanır ve atama yaparken de bir kod bloğu kullanabilirsiniz.
val diye tanımladığımız değişkenler kod çalışırken en az bir kez yazılacak ve belki bir kere, on kere, yüz kere, bin kere okunacak belki de hiç okunmayacak(!). Madem okunmaya da bilir o zaman okunmayacak birşey okunana kadar atamazsak hafızayı daha az kullanmış oluruz. Onun için değişkeni lazy val ile tanımlayabiliriz. Bu yöntem hafıza kullanımı açısından faydalı olsa da cpu’yu normal bir atama gibi değil de daha fazla meşgul ediyor. Nedeni her erişimde en azından bir kez atama yapıldı mı yapılmadı mı diye kontrol etmek gerekiyor bir de ufak bir flag tutmak gerekir atandığını işaretlemek için. Bir de birden fazla thread bu değişkene erişebileceği için bu erişim kontrollü yapılması yani herhangi bir t anında sadece ve sadece bir thread’in erişmesi için gerekli senkronizasyonun işletilmesi gerekir.
Lazy’nin kullanılması gereken ilginç bir örnek vereyim:
scala> abstract class A { val x: String 
     | println("x len: " + x.length)
     | }
defined class A
scala> object B extends A { val x = "Yusuf" } 
defined module B
scala> B
java.lang.NullPointerException
at ...
B‘yi ilk kullanmaya çalıştığımızda NPE’yi yeriz. ;) Ama B‘yi şu şekilde tanımlarsak sorun olmaz:
scala> abstract class A { val x: String 
     | println(x.length)
     | }
defined class A
scala> object B extends A { lazy val x = "Yusuf" } 
defined module B
scala> B
x len: 5
res2: B.type = B$@6a48ffbc
Bunun nedeni A sınıfındaki x‘i B objesinde lazy olarak değiştirdik. Artık x‘e ilk erişildiğinde değeri atanacak. İlk erişildiği nokta ise A sınıfının başlangıcı (initialization’ı). İlk örnekte,x daha tanımlanmadığı için yani B objesi hafızada daha oluşturulmadan x‘e atama yapılmayacağı için null olarak kalacağından NPE oluşur.
Son olarak lazy’nin bir de döngüsel (cyclic) bağımlılıklarda nasıl işe yaradığını görelim ve macerayı tamamlayalım. Aşağıda birbirine bağımlı sınıf tanımları var:
object CyclicLazy extends App {
 
  trait A { val x: A }
  case class B extends A { val x = C() }
  case class C extends A { val x = B() }
  println(B().x)
}

B ve C sınıfları birbirine bağımlı. Bunların başlangıc kod bloklarında ki val x = ... kısmını lazy val x = ... şekline dönüştürmezsek StackOverflowError alırız. Lazy’yi bu örnekte olduğu gibi döngüsel bağımlılıklardan kaynaklanan durumları önlemek için de kullanabiliriz.
zafer.teker , 18.05.2018

Bu Sayfayı Paylaş:

Fibiler Üyelerinin Yorumları


Tüm üyeler içeriklere yorum ekleyerek katkıda bulunabilir : Yorum Gir

Misafir Yorumları




Bu Sayfayı Paylaş:

İletişim Bilgileri

Takip Et

Her Hakkı Saklıdır. Bu sitede yayınlanan tüm bilgi ve fikirlerin kullanımından fibiler.com sorumlu değildir. Bu sitede üretilmiş , derlenmiş içerikleri, fibiler.com'u kaynak göstermek koşuluyla kendi sitenizde kullanılabilirsiniz. Ancak telif hakkı olan içeriklerin hakları sahiplerine aittir