Java - Jenerikler ( Generics )

Pazar, Mayıs 24, 2015 kustemura 0 Yorum


Merhaba , bu yazımda Jenerikler(Generics)'den bahsedeceğim. Jenerikler Java'ya JDK 5 ile eklenmiştir.
Jeneriklerin kullanıldığı kaynak kodlar javac derleyicisinin daha önceki versiyonlarında derlenemez.
Jenerikler, Java'yı iki önemli bakımdan değiştirmiştir.Birincisi , bu yeni özellik dile yeni bir sözdizimi öğesi eklemiştir.İkincisi ise , çekirdek API'daki birçok sınıf ve method'da değişikliklere neden olmuştur.Java kodlarının yazılma biçimini temelden değiştirmiştir.
Temel olarak jenerikler terimi , parametreli tipler anlamına gelir.Bu yapı önemlidir , üzerinde çalışacakları veri tipinin bir parametre olarak belirtildiği sınıflar , arabirimler ve methodlar oluşturabilmenizi sağlar.
Jenerikler'den önce Object tipindeki referanslar yoluyla yazılan kaynak kodları hala günümüzde kullanıma devam etmektedir.Object tipindeki sorun tip güvenliği olmamasıydı.Jenerikler bu soruna çözüm olarak tasarlanmıştır.Ayrıca süreci hızlandırır artık Object ve üzerinde çalışan tip arasında dönüşüm yapılmasına gerek duyulmaz.Jeneriklerde tüm tip atamaları otomatik ve kapalı olarak yapılır.Bu şekilde Jenerikler kodunuzu tekrar kullanabilme yeteneğinizi arttırır , bunu güvenle ve kolaylıkla yapabilmenizi sağlar.Bir örnek üzerinden inceleyelim.

package com.blogger_project;
class NonGen {
 Object type;

 NonGen(final Object type) {
  this.type = type;
 }

 Object getObject() {
  return type;
 }

 void showType() {
  System.out.println("Object type'ın tipi :" + type.getClass().getName());
 }

}

class NonGenDemo {
 public static void main(final String args[]) {
  final NonGen intType = new NonGen(35);
  final int v = (Integer) intType.getObject();
  intType.showType();
  System.out.println("Değeri :" + v);

  System.out.println();

  final NonGen strType = new NonGen("Hello World!");
  final String str = (String) strType.getObject();
  strType.showType();
  System.out.println("Değeri : " + str);

  System.out.println();

  final NonGen doubleType = new NonGen(12.5);
  final Double d = (Double) doubleType.getObject();
  doubleType.showType();
  System.out.println("Değeri : " + d);

 }
}
Programın Çıktısı : 
Object type'ın tipi :java.lang.Integer
Değeri :35

Object type'ın tipi :java.lang.String
Değeri : Hello World!

Object type'ın tipi :java.lang.Double
Değeri : 12.5


Yukarıdaki programda Object tanımlamaları olduğu için NonGen'in jenerik versiyon gibi her tipte nesneyi depolayabilmesini sağlar.Ancak aynı zamanda java derleyicisinin gerçekte NonGen içerisinde depolanan verinin tipi hakkında gerçek bir bilgiye sahip olmasını önler.Bu , iki nedenle kötüdür.Birincisi depolanan veriyi elde etmek için açık tip atamaları gereklidir.İkincisi ise çalışma zamanında çeşitli türlerde tip uyuşmazlığı hataları oluşabilir.

int v = (Integer) intType.getObject();

Burada , getObject() methodunun dönüş tipi Object olduğundan , değerin kutudan çıkartılıp v değişkeninde depolanmasını sağlamak için Integer'a tip ataması gereklidir.Tip atamasını kaldırırsanız program derlenmemez.

intType = strType;
v = (Integer) intType.getObject();

Burada bir çalışma zamanı hatası oluşur.Çünkü intType'a strType atanır.Ancak strType bir tamsayı değil , karakter katarı içeren bir nesneye referans yapar.Bu atama sözdizimsel açıdan geçerli değildir.Çünkü tüm NonGen referansları aynıdır ve herhangi bir NonGen referansı bir başka NonGen nesnesine referans yapabilir.Ancak bu mantıksal olarak yanlıştır.getObject() methodunun döüş tipi tip atamasıyla integer yapılır ve sonra bu değeri v değişkenine atama yapılır.Sorun artık intType'in bir Integer değil bir String içeren nesneye referans yapmasıdır.İşte burada Jenerikler kullanılmadan java derleyicisinin bunu bilmesine imkan yoktur ve bir çalışma zamanı hatası ortaya çıkmıştır.Jeneriklerde aynı işlem denenseydi , derleyici sorunu yakalayacak ve bir hata bildirecekti.Bu da bir çalışma zamanı hatasını önleyecekti.Jenerikler sayesinde oluşan çalışma zamanı hataları , artık derlenme zamanı hatası haline gelmiştir.

Bir programcının en çok korktuğu şeydir bu! Eğer iyi bir programcı olmak istiyorsak , kullanacağımız herşey tüm kurallara uygun olmalıdır.İyi bir programcı çalışma zamanı hatalarına izin vermemelidir.Bir işi yapan her program doğru sonuçlar verebilir fakat doğru kullanıma sahip değildir  :)

Şimdi yukarıda ki programın Jenerik versiyonunu oluşturalım.

package com.blogger_project;

class Gen<T> {
 T type;

 Gen(final T type) {
  this.type = type;
 }

 T getType() {
  return type;
 }

 void showType() {
  System.out.println("Jenerik type'ın tipi : "+ type.getClass().getName());
 }

}

class GenDemo {
 public static void main(final String args[]) {
  final Gen<Integer> intType = new Gen<Integer>(35);
  intType.showType();
  final int v = intType.getType();
  System.out.println("Değeri :" + v);

  System.out.println();

  final Gen<String> strType = new Gen<String>("Hello World!");
  strType.showType();
  final String str = strType.getType();
  System.out.println("Değeri : " + str);

  System.out.println();

  final Gen<Double> doubleType = new Gen<Double>(12.5);
  doubleType.showType();
  final Double d = doubleType.getType();
  System.out.println("Değeri : " + d);
 }
}
Programın Çıktısı :
Jenerik type'ın tipi : java.lang.Integer
Değeri :35

Jenerik type'ın tipi : java.lang.String
Değeri : Hello World!

Jenerik type'ın tipi : java.lang.Double
Değeri : 12.5

class Gen<T> { ..

Bu kullanıma dikkat edelim.
T , bir tip parametresinin adıdır.Bu ad nesne oluşturulurken Gen sınıfına aktarılacak olan gerçek tip için bir yer tutucudur.Bu yüzden type , T'ye akratılan tipte bir nesne olucaktır.Örnekte olduğu gibi T'ye aktarılan tip Integer,String ve Double tipleri , type'ı o tipe ait bir nesne yapıyor.Java derleyicisinin gerçekte Gen'in ya da bir başka jenerik sınıfın değişik versiyonlarının oluşturulmadığını belirtmemiz gerekir.Derleyici , tüm jenerik tip bilgilerini ortadan kaldırır.Bu işleme silme( erasure ) denir.

Gen<Integer> intType = new Gen<Integer>(35);

Bu satır Gen constructoru çağrıldığında , Integer tip argümanının da belirtildiğine dikkat edelim.Bu gereklidir , çünkü referans atanacağı nesnenin (intType) tipi Gen < Integer >'dır.Bu yüzden new tarafından döndürülen referansda aynı tipe sahip olmalıdır.Değilse bir derleme zamanı hatası gerçekleşir.

Gen<Integer> intType;
intType = new Gen<Double>(35);

Yukarıdaki kod buna bir örnektir.Nesne Gen tipinde olduğundan Gen tipinde bir nesneye başvuramaz.Tip kontrolü Jeneriklerin en başlıca avantajlarından biridir.

Jenerikler sadece nesnelerle çalışır.Jenerik bir tipte örnek deklare ederken , tip parametresine aktarılan tip sınıf tipi olmalıdır.int yada char gibi bir primitif tip kullanılamaz.


Gen<int> intType = new Gen<int>(35);
Örnek olarak Gen sınıfında , T'ye herhangi bir sınıf tipi aktarabilirsiniz,ancak tip parametresine bir primitif tip aktaramazsınız.


Jenerikler iki tip parametreli olarakda kullanılabilir.Aşağıdaki programı inceleyelim.

package com.blogger_project;

class TwoGen<T, Y> {
 T type;
 Y type2;

 TwoGen(final T type, final Y type2) {
  this.type = type;
  this.type2 = type2;
 }

 T getType() {
  return type;
 }

 Y getType2() {
  return type2;
 }

 void showType() {
  System.out.println("Jenerik type'ın tipi : "
    + type.getClass().getName());
  System.out.println("Jenerik type2'ın tipi : "
    + type2.getClass().getName());
 }

}

class GenDemo {
 public static void main(final String args[]) {
  final TwoGen<Integer, String> twoType = new TwoGen<Integer, String>(35,
    "Hello World!");
  twoType.showType();
  final int v = twoType.getType();
  System.out.println("Değeri (int):" + v);

  final String str = twoType.getType2();
  System.out.println("Değeri (String):" + str);

 }
}
Programın Çıktısı : 
Jenerik type'ın tipi : java.lang.Integer
Jenerik type2'ın tipi : java.lang.String
Değeri (int):35
Değeri (String):Hello World!

0 yorum :