Kafayı yiyecektim arkadaşlar. Şaka gibi ama, Java'nın Generics'i bizi kandırıyor! "Tip güvenliği" diye diye övündüğümüz o sistem, runtime'a gelince buharlaşıyor. Meğerse type erasure denen bir canavar varmış ve derleme zamanında (compile-time) bütün o güzelim `<Integer>`, `<String>`'leri siliyormuş. Geriye sadece ham List, Map kalıyormuş.
Şu kodu bir düşünün:
Java:
List<Integer> integerList = new ArrayList<>();
integerList.add(42);
// Derleyici burada mutlu, tip güvenli.
Derleyici (compiler) bu kodu görünce "Aferin, sadece `Integer` ekliyorsun" diye bizi tebrik ediyor. Ama `.class` dosyasına yazılan ve runtime'da çalışan bytecode'a baktığınızda manzara şu:
Java:
List list = new ArrayList(); // Bakın, Integer yok!
list.add(42); // Aslında bu Object olarak ekleniyor.
Erasure işte tam olarak bu. Derleyici, kodu kontrol edip doğruladıktan sonra, generics bilgisini (type parameters) atıyor. Runtime'da JVM'in bundan haberi yok! Bu, reflection kullanarak runtime'da listeye `String` fırlatabileceğiniz anlamına geliyor. Tabii ClassCastException'ı davet ederek!
İşte asıl komedi (ya da trajedi) burada başlıyor. Tip güvenliği derleme zamanında sağlandığı için, runtime'da hatalı bir şekilde `String` ekleseniz bile, derleyici sizi uyarmaz. Ama siz o listeden bir elemanı `Integer` diye almaya çalıştığınız an, JVM içerdeki nesnenin aslında `String` olduğunu görür ve size meşhur istisnayı fırlatır.
Java:
// Kötü niyetli reflection örneği
List<Integer> myList = new ArrayList<>();
myList.add(123);
Method addMethod = myList.getClass().getMethod("add", Object.class);
addMethod.invoke(myList, "HACK!"); // Derleme hatası YOK!
Integer num = myList.get(1); // BUM! ClassCastException: String cannot be cast to Integer
StackOverflow'da bile bu tür soruları ararken "Bu nasıl olur?" diye sorguladım kendimi. Meğerse sorun, JVM'in geriye uyumluluğu korumak için bu yolu seçmesiymiş.
Var tabii, ama sihirli değil. Runtime'da tip bilgisine ihtiyacınız varsa, "Reifiable Types" denen (örneğin, `List<String>[]` yerine `List<?>[]` kullanmak) veya "Super Type Tokens" gibi pattern'lar kullanmanız gerekiyor. Ya da kabullenip, dönüşüm (casting) yaparken iki kere düşüneceksiniz.
Sonuç olarak, Java Generics bize derleme zamanında harika bir güvenlik ve okunabilirlik sunuyor. Ama runtime'da bir hayalet gibi davranıyor. Bu dansı bilmeyen herkes, bir noktada ClassCastException'a tökezlemeye mahkum.
Siz de runtime'da generics ile uğraşırken böyle saçmalıklar yaşadınız mı? `List<Integer>`'in aslında `List` olduğunu unutup reflection'la başınızı derde soktunuz mu? Daha temiz çözümleri olan varsa yoruma yazsın, hep birlikte öğrenelim!