Oca 24
Java 1.5 ile gelen yenilikler içerisinde bulunan Genel Tipler ile bazı kod yazma pratiklerimizi değiştirmek ve kendimizi geliştirmek zorunda kaldık. Data Access Object ve Service gibi Üst Seviye Tasarım Kalıplarında (Enterprise Design Patterns), Java Torbalarında (Collections & Map) çok sık kullanır olduk. Tasarımsal olarak getirdiği faydalar olduğu gibi kötü kullanımda da kod okunabilirliliğini azaltmaktadır. Gelin Genel Tipler'in nasıl kullanıldığını bir bakalım...  Yazının diğer bölümleri : Bölüm-2
[--split--]
Nedir bu genel tipler (Generic Types)? Beynimizi çok yormaya gerek yok. Java 1.5 öncesinde bir liste oluşturduğumuzda ve bu listeden bir nesne almak istediğimizde zahmetli bir işe kalkışırdık :
    1:   List liste = new ArrayList();
    2:   liste.add(new Integer(0));
    3:   liste.add(new Integer(1));
    4:   for(int i=0; i<liste.size(); i++){
    5:        Integer sayi = (Integer) liste.get(i);
    6:   }

6. satırda olduğu gibi istediğimiz tipe dönüşüm yaparak (upcasting / downcasting) istediğimiz nesneyi kullanabilirdik.
     1:    List liste = new ArrayList();
    2:   liste.add(new Integer(0));
    3:   liste.add(new Integer(1));
    4:   liste.add(new String("test"));
    5:   for(int i=0; i<liste.size(); i++){
    6:        Integer sayi = (Integer) liste.get(i);
    7:        System.out.println("["+i+"] = "+ sayi);
    8:   }

Sonuç:
[0] = 0
[1] = 1
Exception in thread "main" java.lang.ClassCastException: java.lang.String
    at com.articles.generics.objectcaching.ObjectPoolManager.main(ObjectPoolManager.java:67)

Oluşturduğumuz liste nin sadece Integer tipindeki nesneleri içereceğini düşünürken takım arkadaşımız başka bir yordam içerisinde öyle bir kod yazmışki bu liste içerisine String tipinde bir nesne atmış ( 4. satır ). Çalışma zamanında (Runtime) bu liste içerisindeki nesneleri Integer tipine dönüştürerek alırken String tipinde bir nesnenin gelmesi hata fırlamasına ve uygulamamızın durmasına neden olmaktadır.


Java 1.5 ile gelen genel tipler ( Generics ) bize tam burada yardımımıza koşmaktadır : çok sık kullandığımız Collection ve Map hiyerarşisindeki torbaların (containers) kullanımında yukarıdaki sorunların ortadan kalkması...
     1:    List<Integer> intList = new ArrayList<Integer>();
     2:    intList.add(new Integer(1));
     3:    intList.add(new Integer(2));
     4:    intList.add(new String("3"));

Satırları javac ile derlemeye çalıştığımızda :

şeklinde derleme uyarısı alırız. Çünkü 1. satırda <Integer> ifadesi (tip parametresi) ile oluşturacağımız listeye Integer tipi üzerinde işlem gerçekleştireceğimizi söyleriz. String tipinde bir nesneyi listeye koymak istediğimizde ise derleme hatası alırız. Böylece çalışma zamanında (Runtime) beklenmedik tip dönüşümlerinden meydana gelen hatalardan kurtulmuş oluruz.
Nesne Havuzu ( Object Pool )

Jenerik yapıları anlamak için gelin bir uygulama geliştirelim. Bu uygulama veritabanı bağlantı havuzu (DB Connection Pool) yada iş parçacıkları havuzu (Thread Pooling) gibi asıl amacı sürekli tekrarlayan işlemlerde belli bir tipteki nesnelerin her seferde oluşturulması ve silinmesini engellemektir. Böylece uygulamada gereksiz yavaşlamaları engelleyebilir ve nesne takibini iyileştirebiliriz.


Gelin öncelikle uygulamamızı süper yapıları tanımlayarak başlayalım. Bu süper yapılar : arayüzler (interface), süper sınıflar (super class) ve soyut sınıflar (abstract class) olabilir.
Süper yapılara üzerinden uygulamamızı geliştirdiğimiz zaman ileriye yönelik geliştirme ve sistemler arası iletişim daha kolay yapılmış olacaktır.


Oluşturacağımız süper yapılar:

1.      Yedeklenebilir Arayüzü (Interface) : Sistem tarafından havuzda tutalacak olan nesnelerin sınıf tanımlamaları bu arayüzü gerçekleştirmelidir.
2.      Havuz Arayüzü (Interface) : doldur(), cek()/koy(), bos() soyut yordamlarını içeren arayüzdür. Bu arayüzü gerçekleştiren sınıflar <<Yedeklenebilir>> arayüzünü gerçekleştirmiş yapılar için sınırlandırılmalıdır.
3.      HavuzYöneticisi Arayüzü (Interface) : nesneCek(), nesneKoy() ve havuzuKontrolEt() yordamlarını içermektedir.
    a.      nesneCek() yordamı istenen nesnenin tipine göre ilgili havuzdan nesne çekecek
    b.      nesneKoy() yordami gönderilen nesneyi, nesnenin tipine göre ilgili havuza geri koyar.
    c.       havuzuKontrolEt() yordami nesneCek() yordami çağrıldığında ilgili nesne için havuz nesnesinin yaratılıp içerisinin doldurulmasını kontrol eder.


          
 
Havuz Arayüzü


/**
*{@link Yedeklenebilir}arayüzünügerçekleştirmiş
*nesneleriçinhavuzgörevinigörür.
*
*@param<T extends YedeklenebilirNesne>
*/
public interface Havuz <T extends Yedeklenebilir> {
    
     /**
      *Havuzdannesneçeker.
      *@returnT
      */
     T cek();
    
     /**
      *Nesneyigerikoyar.
      *@paramobject
      */
     void koy(T nesne);
    
     /**
      *clazztipindenesneleriyaratipkendisinidoldurur.
      *@paramclazz
      *@throwsNesneYaratmaHatasi
      */
     void doldur(Class<T> clazz) throws NesneYaratmaHatasi;
    
     /**
      *clazztipindenesnelerdenbelirtilenadetkadar
* yaratipkendisinidoldurur.
      *@paramclazz
      *@paramadet
      *@throwsNesneYaratmaHatasi
      */
     void doldur(Class<T> clazz, int adet) throws NesneYaratmaHatasi;
    
     /**
      *Havuzunbosolupolmadigikontroledilir
      *@returnboolean
      */
     boolean bos();
}


Havuz arayüzüne ait yukarıdaki tanımlamada dikkatimizi <T extends Yedeklenebilir> tip parametresi çekmektedir. Böylece Havuz arayüzünü sadece Yedeklenebilir arayüzüne erişmiş yapılar için genelleştirmiş / sınırlandırmış (bounding) olduk.

Taner Diler
taner.diler[et]gmail.com
Oca 25
Java 1.5 ile gelen yenilikler içerisinde bulunan Genel Tipler ile bazı kod yazma pratiklerimizi değiştirmek ve kendimizi geliştirmek zorunda kaldık. Data Access Object ve Service gibi Üst Seviye Tasarım Kalıplarında (Enterprise Design Patterns), Java Torbalarında (Collections & Map) çok sık kullanır olduk. Tasarımsal olarak getirdiği faydalar olduğu gibi kötü kullanımda da kod okunabilirliliğini azaltmaktadır. Gelin Genel Tipler'in nasıl kullanıldığını bir bakalım...  Yazının diğer bölümleri : Bölüm-1
[--split--]

NesneHavuzu Sınıfı


public class NesneHavuzu<T extends Yedeklenebilir>
     implements Havuz<T> {
    
     private Vector<T> havuz = new Vector<T>();


     /* (non-Javadoc)
      * @see com.articles.generics.objectcaching.Havuz#doldur(java.lang.Class)
      */
     public void doldur(Class<T> clazz) throws NesneYaratmaHatasi {
           this.doldur(clazz, 5);      
     }
    
     /* (non-Javadoc)
      * @see com.articles.generics.objectcaching.Havuz#doldur(java.lang.Class, int)
      */
     public void doldur(Class<T> clazz, int adet)
throws NesneYaratmaHatasi {


           try {
                 for(int sayac=0; sayac<adet; sayac++){
                       this.havuz.add(clazz.newInstance());
                 }
           } catch (InstantiationException e) {
                 NesneYaratmaHatasi ex =
new NesneYaratmaHatasi(clazz, e.getStackTrace());
                 throw ex;
           } catch (IllegalAccessException e) {
                 NesneYaratmaHatasi ex =
new NesneYaratmaHatasi(clazz, e.getStackTrace());
                 throw ex;
           }
     }
    
     /*
      * (non-Javadoc)
      *
      * @see com.articles.generics.objectcaching.Pool#pull()
      */
     public T cek() {
           T object = havuz.remove(0);
           return object;
     }


     /*
      * (non-Javadoc)
      *
      * @see com.articles.generics.objectcaching.Pool#push(com.articles.generics.objectcaching.CachableObject)
      */
     public void koy(T object) {
           havuz.add(object);
     }


     /* (non-Javadoc)
      * @see com.articles.generics.objectcaching.Havuz#bos()
      */
     public boolean bos() {
           return this.havuz.isEmpty();
     }}


NesneHavuzu sınıfına dikkat edersek :  Havuz arayüzündeki tip parametresi
<T extends Yedeklenebilir>  burada da mevcut. Neden böyle?  Bakalım:


NesneHavuzu sınıfında <T extends Object> tanımını kullanıp derlemek istediğimizde :
şeklinde bir uyarı ile karşılaşırdık. Bu uyarıda NesneHavuzu sınıfı tanımlamasındaki tip parametresi, Havuz arayüzünün beklediği tip parametresinin sınırları dışında oluduğu belirtilmektedir.

NesneHavuzu sınıfında <T extends X_Nesne> tanımını kullanıp derlemek istediğimizde herhangi sorun ile karşılaşmayız çünkü T tip parametresi Havuz arayüzünün beklediği sınırlar yani Yedeklenebilir arayüzünün alt hiyerarşisi içerisindedir.


Peki Havuz arayüzünün tanılamasını public interface Havuz <T> şeklinde değiştirdiğimiz de ne olur? Birşey olmaz çünkü böyle yaparak Havuz arayüzü üzerindeki kısıtlamayı kaldırıp bu arayüzü her tipten yapı için çalışır duruma getirmiş oluruz, standart Java paketi içerisindeki torba (Hash, List ...) yapılarında olduğu gibi.


Şimdi de şöyle bişey deneyelim. Havuz arayüzünü eski haline getirelim ama bu sefer NesneHavuzu sınıfı tanımını NesneHavuzu<T> implements Havuz<T> şeklinde değiştirelim. Derlemek istediğimiz de


<T extends Object> örneğimizde olduğu gibi yine aynı nedenden dolayı derleme zamanında uyarı  alırız.

Peki alt hiyerarşi sınırlandırması için bir tanımlama mevcutken üst hiyerarşi sınırlandırması için bir tanımlama mevcut mu?  <T super Yedeklenebilir> Yedeklenebilir arayüzünün üst hiyerarşisi için sınırlama yapılır.

Şimdiye kadar yazmış olduğumuz yapıların testini gerçekleştirelim.


public class HavuzTest extends TestCase {
     private X_Nesne x_n1 = new X_Nesne();
     private X_Nesne x_n2 = new X_Nesne();
     private Y_Nesne y_n1 = new Y_Nesne();
     private Y_Nesne y_n2 = new Y_Nesne();
    
     Object obje_1 = new Object();
    
     private NesneHavuzu<X_Nesne> x_havuz = new NesneHavuzu<X_Nesne>();
     private NesneHavuzu<Y_Nesne> y_havuz = new NesneHavuzu<Y_Nesne>();
     private NesneHavuzu<Yedeklenebilir> genel_havuz = new NesneHavuzu<Yedeklenebilir>();
    
     public void testCek() {
           X_Nesne x_nesne_1 = x_havuz.cek();
//          Y_Nesne x_nesne_2 = x_havuz.cek(); Derleme hatasi verir
           Y_Nesne y_nesne_1 = y_havuz.cek();
          
          
//          Cek yordami Yedeklenebilir tipinde değer dönerken
//          biz X_Nesne tipinde bekliyoruz.
//          X_Nesne x_nesne_3 = genel_havuz.cek();
          
           X_Nesne x_nesne_3 = (X_Nesne) genel_havuz.cek();
          
     }


     public void testKoy() {
           x_havuz.koy(x_n1);
           x_havuz.koy(x_n2);
//          x_havuz.koy(y_n1); Derleme hatasi verir
          
           y_havuz.koy(y_n1);
           y_havuz.koy(y_n2);
          
           genel_havuz.koy(x_n1);
           genel_havuz.koy(y_n1);
    
//          x_havuz.koy(obje_1);         DERLEME HATASI
//          y_havuz.koy(obje_1);         DERLEME HATASI
//          genel_havuz.koy(obje_1);     DERLEME HATASI
     }
}


HavuzYoneticisi Arayüzü

public interface HavuzYoneticisi {
    
<T extends Yedeklenebilir> void nesneKoy(T nesne) throws NesneYaratmaHatasi;
    
<T extends Yedeklenebilir> T nesneCek(Class<T> clazz) throws NesneYaratmaHatasi;
    
<T extends Yedeklenebilir> void havuzuKontrolEt(Class<T> clazz) throws NesneYaratmaHatasi;
}

Burada dikkat etmemiz gereken, arayüzü değil arayüz içerisindeki yordamları genelleştiriyor olmamız. <T extends Yedeklenebilir> tip parametresi korunarak yine hiyerarşi sınırlandırması yapmış oluyoruz. Eğer HavuzYoneticisi arayüzünü de <T extends Yedeklenebilir> tip parametresi ile genelleştirmiş olsaydık sadece tek tip bir yapı için çalışıyor hale gelirdi. Tıpkı Havuz arayüzü gibi :

           NesneHavuzu<X_Nesne> x_havuz = new NesneHavuzu<X_Nesne>();


NesneHavuzuYoneticisi Sınıfı

public class NesneHavuzuYoneticisi implements HavuzYoneticisi {


     private Map<Class<? extends Yedeklenebilir>,
Havuz<? extends Yedeklenebilir>>
havuzMap = new HashMap<
Class<? extends