Java - Jenerikler ( Generics )
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 :