Scala'da Implicit Conversion'lar

scala-logo scala-logo   Bu aralar Scala'ya merak sardım.

Diyelim program yazıyorsunuz. Bir metot çağıracaksınız ve A tipinde bir veri istiyor. Sizde ise A'ya dönüştürülebilir B tipinde bir veri var.

Normalde ne yaparsınız? B'yi A'ya çeviren bir metot yazarsınız ve önce bunu çağırarak verinizi A tipine dönüştürürsünüz.

Şunun gibi:

public A covertBtoA(B variable) {
  ...
  return toA;
}
public void somethingWithA(A variable) {
  ...
}
B myVariable = ...something...;
somethingWithA(convertBtoA(myVariable));

Bu dönüşümü çeşitli yerlerde sürekli yaptığınızı düşünün. Sonra bunu otomatikleştirebileceğinizi düşünün.

Scala'da bir tipten bir tipe dönüşüm yukarıdaki gibi el ile yapılabileceği gibi "implicit conversions" ile otomatik hale de getirilebilir. Bunu bir örnek üzerinden açıklayayım.

Projenizde bir kütüphane kullanıyorsunuz ve bu kütüphanede imzası aşağıdaki gibi olan bir metot var:

def parseStream(stream: InputStream)

Sizin bu metotta kullanmak istediğiniz veri ise bir Stream'da değil, bir String nesnesi içerisinde.

val myDataToBeParsed = "Ben bu veriyi parse ettirmek istiyorum."

parseStream'ın imzasını değiştiremezsiniz çünkü kontrol sizde değil, bir kütüphanenin parçası. O zaman myDataToBeParsed'i bir IntputStream'a çevirmelisiniz. Bunu şu şekilde yapabilirsiniz:

val myDataToBeParsedAsInputStream = new ByteArrayInputStream(value.getBytes("UTF-8"))

Daha sonra myDataToBeParsedAsInputStream ile parseStream metodunu çağırabilirsiniz:

myDataToBeParsed(myDataToBeParsedAsInputStream)

Ancak bu iki adımlı bir işlem. Eğer uygulamanızın birçok yerinde bu String -> InputStream dönüşümü tekrar edecekse can sıkıcı olabilir. Bu işlem otomatik olursa çok daha kolay ve anlaşılır olmaz mı?

Dönüşümleri otomatik yapabilmek için öncelikle bir singleton nesne içerisine istenilen dönüşümleri gerçekleştirecek metotlar yazılır:

object StringImplicits {
  implicit def String2InputStream(value: String): InputStream = new ByteArrayInputStream(value.getBytes("UTF-8"))
}

StringImplicits ve String2InputStream örnek bir isimlerdir. Herhangi bir isim verilebilir. Metot tanımının en başında bulunan implicit bunun otomatik olarak uygulanacak bir dönüşüm kuralı olduğunu Scala'ya belirtir. Scala bu metodun girdi ve çıktı parametresini inceleyerek, metodun String tipini InputSteam'a dönüştürdüğünü kendisi anlar.

Bu tanımlamayı yaptıktan sonra artık kodumuzu şu şekilde düzenleyebiliriz:

import StringImplicits._
myDataToBeParsed(myDataToBeParsed)

myDataToBeParsed String tipine olmasına ve myDataToBeParsed InputStream beklemesine rağmen bu kod derlenir ve doğru bir şekilde çalışır. import StringImplicits._ satırı sayesinde otomatik dönüşüm kuralları arka planda otomatik olarak çalışmaktadır.

import StringImplicits._ ile dönüşüm kurallarını kodu yazdığınız kısımda görünür hale getirmezseniz kodunuz derlenmez, dönüşüm yapılmaz. Bunun otomatik olmamasının önemli bir sebebi vardır: Büyük uygulamalarda otomatik dönüşüm sayısı arttıkça istenmeyen dönüşümler olabilir, geliştiriciler uygulamanın kontrolünü kaybedebilirler, hata oranı artabilir.

Bu yüzden kullanılacak dönüşüm kuralları önceden import edilmelidir.