Generics Neden Var
Generic bir tip, kodu bir kez yazıp tip güvenliğinden ödün vermeden birçok tip için yeniden kullanmanı sağlar. Ayrı ayrı bir IntBox, bir StringBox ve bir UserBox yazmak yerine, T'nin çağıranın doldurduğu bir yer tutucu olduğu tek bir Box<T> yazarsın.
Aslında her ArrayList<String> ya da HashMap<String, Integer> yazdığında generics kullandın. <...> kısmı bir tip argümanıdır. Bu sayfa kendi generic tiplerini nasıl yazacağını gösteriyor.
Alternatif - her şeyi Object olarak saklamak - tip bilgisini çöpe atar ve çirkin, hataya açık tip dönüşümlerini zorunlu kılar:
Son satırdaki o tip dönüşümü çalışma zamanında bir ClassCastException fırlatır - tam da generics'in imkansız kılmak için tasarlandığı türden bir hata.
Generic Bir Sınıf
Sınıf adından sonra açılı parantezler içinde bir tip parametresi tanımla. Geleneksel olarak tek bir büyük harf kullanılır: "type" (tip) için T, "element" (eleman) için E, anahtar/değer için K/V.
Box içinde her T, çağıranın sağladığı her ne ise ona dönüşür. Box<String> yalnızca String tutan ve döndüren bir kutudur. Derleyici, program daha çalışmadan name.set(99)'u reddeder.
Sağdaki boş <> (yani elmas operatörü) derleyicinin tip argümanını taraftan çıkarsamasını sağlar, böylece <String>'i iki kez tekrarlamazsın.
Generic Metotlar
Tek bir metot, sınıftan bağımsız olarak kendi tip parametresine sahip olabilir. <T> parametresini dönüş tipinden önce koy:
T'yi asla açıkça geçmezsin - derleyici onu argümandan çıkarsar. Generic metotlar, Collections.sort ya da List.of gibi yardımcı araçların herhangi bir eleman tipinde tip güvenli kalmasının yoludur.
Sınırlandırılmış Tip Parametreleri
Bazen generic bir tip yalnızca bazı tipler için anlamlıdır. extends parametreyi kısıtlar, böylece sınırın metotlarını çağırabilirsin. Burada T extends Number, T'nin Number ya da herhangi bir alt sınıf (Integer, Double, ...) olduğu anlamına gelir, dolayısıyla doubleValue() kullanılabilir:
Buradaki extends'in "bir alt tipidir" anlamına geldiğine ve hem sınıflar hem de arayüzler için çalıştığına dikkat et - elemanları karşılaştırman gerektiğinde <T extends Comparable<T>> son derece yaygındır.
Joker Karakterler: ? extends ve ? super
İnce bir tuzak: Integer bir Number olsa da, List<Integer> bir List<Number> değildir. Generics değişmezdir (invariant). Joker karakterler, yalnızca okuman ya da yalnızca yazman gerektiğinde bunu gevşetir.
Okuduğun bir üretici için ? extends T, yazdığın bir tüketici için ? super T kullan ("PECS" kuralı - Producer Extends, Consumer Super):
Bir ? extends Number listesi elemanları Number olarak okumana izin verir ama ona ekleme yapmana izin vermez (derleyici tam eleman tipini bilemez). Bir ? super Integer listesi Integer eklemene izin verir ama okumalar Object olarak geri gelir. Verinin nasıl aktığına uyan joker karakteri seç.
Tip Silme ve Sınırları
Generics bir derleme zamanı özelliğidir. Derlemeden sonra tip parametresi silinir - çalışma zamanında Box<String> ve Box<Integer> ikisi de sadece Box'tır. Bu, generics'i eski kodla geriye dönük uyumlu tutar ama gerçek sınırlar getirir.
// Bunların hiçbiri derlenmez - tip parametresi çalışma zamanında var olmaz:
T value = new T(); // bir tip parametresi örneklenemez
T[] array = new T[10]; // generic bir dizi oluşturulamaz
if (list instanceof List<String>) { } // tip argümanı test edilemez
Tip çalışma zamanında yok olduğu için reflection aracılığıyla "T neydi?" diye soramazsın ve yalnızca generic argümanlarıyla ayrışan metotları aşırı yükleyemezsin (foo(List<String>) ve foo(List<Integer>) aynı imzaya silinir). Tipe gerçekten çalışma zamanında ihtiyacın olduğunda, bir Class<T> belirtecini yapıcı ya da metot parametresi olarak geç.
Sonraki: Lambda İfadeleri
Generic bir metodun parametre olarak bir tip aldığını gördün. Bir sonraki adım davranışı bir parametre olarak ele almaktır. Lambda ifadeleri bir kod parçasını - bir fonksiyonu - bir metoda geçirmeni sağlar; bu tam da yeni öğrendiğin tip güvenli generic koleksiyonları sıralamanın, filtrelemenin ve dönüştürmenin yoludur.
Sıkça Sorulan Sorular
Java'da generics nedir?
Generics, bir sınıfı ya da metodu tek bir somut tipe kilitlemek yerine, daha sonra belirlediğin bir tiple çalışacak şekilde yazmanı sağlar. Açılı parantezler içinde bir tip parametresi tanımlarsın - class Box<T> - ve çağıran taraf gerçek tipi doldurur - Box<String>. Derleyici bu tipi her yerde zorunlu kıldığı için uyumsuzlukları derleme zamanında yakalar ve elle tip dönüşümlerinden kurtulursun.
Object yerine neden generics kullanmalı?
Object kullanmak tüm tip bilgisini kaybeder: derleyici yanlış bir şey koymanı engelleyemez ve dışarı çıkan her değeri tip dönüşümüyle almak zorunda kalırsın (çalışma zamanında ClassCastException riskiyle). Generics bu denetimi derleme zamanına taşır. Bir List<String> bir Integer'ı kabul etmez ve get() zaten bir String döndürür - tip dönüşümü yok, çalışma zamanında sürpriz yok.
Java generics'te tip silme (type erasure) nedir?
Tip silme, generic tip bilgisinin yalnızca derleme zamanında var olması demektir. Derlemeden sonra List<String> ve List<Integer> çalışma zamanında ikisi de sadece List'tir - tip parametresi silinir. Bu yüzden new T[10] yazamaz, list instanceof List<String> çağıramaz veya bir tip parametresini reflection ile okuyamazsın. Generics sana derleme zamanı güvenliği sunar, çalışma zamanı tip verisi değil.