# Java Generics (Jenerikler) Jenerikler, kelime anlamı itibariyle **parametrelendirilmiÅ** tür anlamına gelir. Jenerikler sayesinde, sınıf, arayüz veya metot yazarken tek bir türe baÄlı kalmayıp üzerinde iÅlem yapacaÄınız türü parametre olarak alabilirsiniz. Bu sayede, farklı türler üzerinde çalıÅan tek bir sınıf yazmak mümkündür. Bu Åekilde yazılan sınıflara **jenerik sınıf**, metotlara **jenerik metot** denir. Jenerikler JDK 5 ile dile eklenmiÅtir. Buna raÄmen, Javaânın en temel özelliklerinden biridir ve dili temelden etkilemiÅtir. Bu yüzden, jenerikleri iyi anlamak Javaâyı öÄrenmek açısından büyük önem taÅır. Jenerikler **tür güvenliÄini** (type-safety) saÄlamak amacıyla dile eklenmiÅtir. Javaânın **tür kesinliÄi olan** (strongly typed) bir dil olduÄunu daha önce belirtmiÅtik. Fakat bu iki kavram birbirinden farklıdır. Tür güvenliÄi kavramını ve neden gerekli olduÄunu anlamak için bir örnek yapalım. **_Nullable_** adında basit bir sınıf yazalım. Bu sınıfı NullPointerException hatalarının önüne geçmek amacıyla kullanacaÄız. İlk olarak bu sınıfı String deÄerler üzerinde kullanalım: ```java public class Nullable { private final String value; public Nullable(String value) { this.value = value; } public String getValue() { return value; } public boolean isNull() { return value == null; } @Override public String toString() { return isNull() ? "null" : value; } } ``` String nesneler üzerinde uÄraÅırken null hatalarının önüne geçmek için bu sınıfı kullanacaÄız. Sınıfı oluÅtururken parametre olarak bir **_String_** deÄer vereceÄiz. Bu deÄerin **null** olup olmadıÄını **_isNull()_** metoduyla kontrol edeceÄiz. Åimdi örnek bir kullanım gösterelim: ```java Nullable x = new Nullable("null olmayan deÄer"); if (!x.isNull()) { System.out.println(x.getValue()); } String nullString = null; Nullable y = new Nullable(nullString); if (y.isNull()) { System.out.println("y deÄiÅkeni null"); } ``` Yukarıdaki kodu çalıÅtırdıÄınız zaman çıktısı aÅaÄıdaki gibi olur: ```java null olmayan deÄer y deÄiÅkeni null ``` GördüÄünüz gibi yazdıÄımız sınıf güzel bir Åekilde çalıÅmaktadır. Peki ya bu sınıfı _String_ türünden baÅka türler için de kullanmak istersek? Mevcut durumda _Nullable_ sınıfı yalnızca _String_ deÄerleri kabul ediyor. Yani bu sınıfı Date türündeki deÄerler üzerinde kullanamayız. O halde bu sınıfın adını _NullableString_ olarak deÄiÅtirelim ve _NullableDate_ adında baÅka bir sınıf oluÅturalım: ```java import java.util.Date; public class NullableDate { private final Date value; public NullableDate(Date value) { this.value = value; } public Date getValue() { return value; } public boolean isNull() { return value == null; } @Override public String toString() { return isNull() ? "null" : value.toString(); } } ``` GördüÄünüz gibi, aynı sınıfı sadece **_String_** türünü **_Date_** olarak deÄiÅtirerek tekrar yazdık. Bunun iyi bir yöntem olmadıÄını kabul etmelisiniz. Yalnızca kod tekrarı yapmakla kalmadık, aynı zamanda birbiriyle alakalı olmalarına raÄmen sınıflara farklı isimler vermek zorunda kaldık. Artık **_String_** türü için **_NullableString_** sınıfını, **_Date_** türü için **_NullableDate_** sınıfını kullanabiliriz. Yine de hâlâ ideal bir çözüm bulamadık. Ãünkü Nullable sınıfını yalnızca 2 tür için kullanabiliyoruz. Peki ya bu sınıfı Integer, Double, Boolean gibi diÄer türler için de kullanmak istersek? Hepsi için aynı kodu kopyalayıp farklı sınıflar oluÅturmamız gerekir. Yapmak istediÄimiz Åey, bütün türler için geçerli olacak bir **_Nullable_** sınıfı yazmak. Bunu Åu Åekilde baÅarabiliriz: **_Nullable_** sınıfının **_Object_** türü üzerinde çalıÅmasını saÄlayalım. BildiÄiniz gibi, **_Object_** sınıfı bütün sınıfların atasıdır. Dolayısıyla bütün türleri **_Object_** türünden ifade edebiliriz. Åimdi sınıfı düzenleyip tekrar yazalım: ```java public class Nullable { private final Object value; public Nullable(Object value) { this.value = value; } public Object getValue() { return value; } public boolean isNull() { return value == null; } @Override public String toString() { return isNull() ? "null" : value.toString(); } } ``` Åimdi bu sınıfı farklı türler üzerinde kullanalım: ```java Nullable nullableString = new Nullable("abc"); Nullable nullableDate = new Nullable(new Date()); Nullable nullableInt = new Nullable(2020); Nullable nullableDouble = new Nullable(3.14); Nullable nullableBoolean = new Nullable(true); ``` Yukarıda görebileceÄiniz gibi, **_Nullable_** sınıfını farklı türler üzerinde kullanabiliriz. Fakat hâlâ bir sorunumuz var: **_getValue()_** metodunu çaÄırdıÄımız zaman çıkan deÄeri dönüÅtürmek zorundayız: ```java Nullable nullableString = new Nullable("abc"); if (!nullableString.isNull()) { String value = (String) nullableString.getValue(); } ``` Bu önemli bir açıktır. Bu açık yüzünden farkında olmadan hataya sebebiyet verebiliriz. ÃrneÄin, aÅaÄıdaki kodu inceleyelim: ```java Nullable nullableString = new Nullable("abc"); if (!nullableString.isNull()) { boolean value = (boolean) nullableString.getValue(); } ``` Bu örnekte **_Nullable_** sınıfına parametre olarak verdiÄimiz deÄer **_String_** iken, bu deÄeri **boolean** türüne dönüÅtürmeye çalıÅıyoruz. Bu durumda yukarıda da gördüÄümüz **_ClassCastException_** hatasıyla karÅılaÅırız. Bu örnekten Åunu anlıyoruz: Nullable sınıfını bütün türleri kapsayacak Åekilde geliÅtirmemize raÄmen tür güvenliÄini saÄlayamadık. JDK 5âten önce bu gibi durumlar sıkça yaÅanıyordu ve tür güvensiz kodlar yazılıyordu. Jenerikler ile bunun önüne geçmek mümkün olmuÅtur. Jenerikler, bir yandan farklı türler için tek bir kod yazmamızı saÄlarken, diÄer yandan tür güvenliÄini saÄlar.