Java Generics ile ilgili şaşırtıcı sorular hazırlamak için, genellikle anlaşılması zor veya beklenmeyen davranışları olan örnekler kullanılabilir. İşte bazı örnekler:
Sınırlı Tür Parametreleri ile İlgili Soru:
java
Copy code
class Example<T extends Number> {
void test(T value) {
System.out.println(value);
}
}
// Soru: Aşağıdaki ifadelerin hangisi doğru çalışır?
Example<Integer> example1 = new Example<>();
example1.test(123); // A
Example<String> example2 = new Example<>();
example2.test("Hello"); // B
Burada, Example sınıfı yalnızca Number sınıfından türeyen tür parametreleri ile kullanılabilir. Bu nedenle, Integer ile çalışırken sorun yoktur, ancak String ile kullanıldığında bir hata oluşur.
Joker Türler ile İlgili Soru:
java
Copy code
List<?> mysteryList = new ArrayList<Integer>();
mysteryList.add(10); // A
mysteryList.add(null); // B
Bu soruda, mysteryList joker bir türle tanımlanmıştır ve bu türün ne olduğu bilinmediği için, null dışında hiçbir şey eklenemez. Bu, generics'in tür güvenliğini koruma yöntemidir.
Generics Metotlar ile İlgili Soru:
java
Copy code
public static <T> void printList(List<T> list) {
for (T element : list) {
System.out.println(element);
}
}
// Soru: Aşağıdaki çağrıların hangisi doğru?
printList(new ArrayList<String>()); // A
printList(new ArrayList<? extends Number>()); // B
Generics metodlar, belirtilen tür parametresi ile çağrılabilir. Ancak, joker türler (? extends Number gibi) doğrudan tür parametresi olarak kullanılamaz. Bu yüzden, ArrayList<String> ile çağrı geçerli, diğeri ise geçersizdir.
Bu tür sorular, Java Generics'in karmaşıklığını ve tür sisteminin inceliklerini göstermek için kullanışlıdır. Özellikle joker türler ve sınırlı tür parametreleri, sıkça kafa karıştırıcı konulardandır.
Type Erasure ile İlgili Soru:
java
Copy code
List<Integer> intList = new ArrayList<>();
List<String> stringList = new ArrayList<>();
// Soru: intList ve stringList aynı sınıf mıdır?
System.out.println(intList.getClass() == stringList.getClass());
Java'da type erasure (tür silinmesi) nedeniyle, runtime'da intList ve stringList aslında aynı ArrayList sınıfına aittir. Generics, compile-time'da tür güvenliği sağlamak için vardır ve runtime'da tür bilgisi silinir.
Generics ve Inheritance İlişkisi:
java
Copy code
class Animal {}
class Dog extends Animal {}
List<Dog> dogList = new ArrayList<>();
List<Animal> animalList = dogList;
// Soru: Bu atama doğru mu?
Bu soru, Java'da generics'in kalıtım ile nasıl çalıştığını gösterir. List<Dog> bir List<Animal>'a doğrudan atanamaz çünkü Java'da generics, kalıtımla uyumlu değildir (covariant değildir). Bu tür bir atama, compile-time hatasına neden olur.
Wildcard Capturing İle İlgili Soru:
java
Copy code
public static void addToList(List<?> list) {
list.add("test"); // A
list.add(10); // B
}
// Soru: Bu metodun içinde listeye eleman eklenir mi?
Bu durumda, List<?> joker türü, belirsiz bir türü temsil eder ve bu nedenle, null dışında bir şey eklenemez. Bu, joker türlerin kullanımının sınırlamalarını gösterir ve güvenli olmayan tür dönüşümlerini önlemek için tasarlanmıştır.
Bu tür sorular, Java'daki generics'in karmaşık ve bazen sezgisel olmayan davranışlarını anlamak için mükemmeldir. Özellikle type erasure ve wildcard kullanımı, Java programcıları için sıkça karşılaşılan zorluklardandır.
Elbette, Java Generics ile ilgili daha fazla şaşırtıcı soru örnekleri:
Generics Sınırlamaları ve Raw Types:
java
Copy code
List<String> stringList = new ArrayList<>();
List rawList = stringList;
rawList.add(10); // A
String str = stringList.get(0); // B
// Soru: Bu kod parçası ne tür bir problem yaratır?
Burada, rawList raw type (ham tür) olarak tanımlanmış ve stringList'e atanmıştır. Raw type kullanımı, generics'in getirdiği tür güvenliğini ortadan kaldırır. rawList.add(10) ifadesi çalışır, ancak stringList.get(0) çağrısı ClassCastException hatasına neden olur, çünkü stringList'teki elemanlar artık sadece String olmayabilir.
Generic Sınıflar ve Metotlar Arasındaki Fark:
java
Copy code
class GenericClass<T> {
void method(T t) {}
}
// ve
<T> void genericMethod(T t) {}
// Soru: Bu iki generic tanım arasındaki temel fark nedir?
GenericClass<T> sınıfı, tür parametresi T'ye bağlı bir sınıftır ve bu tür parametresi, sınıfın örnekleri arasında sabittir. Öte yandan, <T> void genericMethod(T t) ifadesinde, her metod çağrısında T tür parametresi farklı olabilir. Bu, sınıf düzeyindeki generics ile metod düzeyindeki generics arasındaki önemli bir farktır.
Üst Sınır Wildcard Kullanımı:
java
Copy code
public static void processList(List<? extends Number> list) {
Number num = list.get(0);
list.add(123); // A
}
// Soru: Bu metodun içinde listeye eleman eklenir mi?
List<? extends Number> ifadesi, Number veya onun alt sınıflarından türeyen herhangi bir türü kabul eder. Ancak bu tür listelere yeni bir eleman eklenemez, çünkü ? extends Number joker türü kesin bir tür belirtmez. Yani list.add(123) ifadesi bir compile-time hatasına neden olur.
Generic Sınıf İnstanceof Kullanımı:
java
Copy code
class Box<T> {}
// Soru: Aşağıdaki ifade doğru mudur?
Box<String> box = new Box<>();
boolean result = box instanceof Box<String>;
instanceof ifadesi, runtime'da tür kontrolü yapar. Ancak generics türleri, runtime'da tür bilgisi silindiği için bu tür kontrolü zorlayıcıdır. Bu nedenle, bu ifade compile-time hatasına neden olur.
Wildcards ve Nullable Türler:
java
Copy code
List<?> wildcardList = new ArrayList<>();
wildcardList.add(null); // A
wildcardList.add("test"); // B
Bu durumda, wildcardList joker türle tanımlanmıştır ve null dışında bir şey eklemek mümkün değildir. Bu, wildcard türlerin nullable türleri temsil etmede kullanılabileceğini gösterir.
public class VarargsExample {
public static <T> void printArray(T... array) {
for (T element : array) {
System.out.println(element);
}
}
public static void main(String[] args) {
printArray(1, 2, 3); // Geçerli: Integer türü
printArray("a", "b", "c"); // Geçerli: String türü
}
}
Bu durumda, printArray metodunu hem Integer hem de String türleriyle sorunsuz bir şekilde çağırabilirsiniz. Her iki çağrı da tür güvenliği ilkelerine uygundur.
java
Copy code
List<? extends Number> numbers = new ArrayList<Integer>();
Bu durumda, numbers değişkeni List türünde bir liste referansını temsil eder, ancak bu listenin içeriği sadece null olarak atanabilir. Bu, wildcards ile üst sınırlı (<? extends Number>) bir referansın, içindeki türü bilinmeyen bir alt sınıfın nesnelerini referans etmesinden kaynaklanır.
Ancak, bu listeden öğeleri çekmek mümkündür:
java
Copy code
Number firstElement = numbers.get(0);
Ancak, liste içerisine yeni bir eleman eklemek mümkün değildir, çünkü hangi alt sınıfın kullanıldığı bilinmemektedir.
java
Copy code
numbers.add(null); // Geçerli
numbers.add(new Integer(10)); // Compile-time hatası
Bu nedenle, numbers listesine yalnızca null ekleyebilirsiniz, çünkü bu durumda tür güvenliği sağlanabilir. Ancak, belirli bir tür eklemek compile-time hatasına neden olur.
Bu konsept, wildcards ve generics kullanımındaki tür güvenliği ve esnekliği gösterir.
Type Erasure ve Generic Sınıflar:
java
Copy code
class Example<T> {
private T value;
Example(T value) {
this.value = value;
}
// Soru: Bu metot neden compile-time hatasına neden olur?
T getValue() {
return new T(); // A
}
}
Bu durumda, type erasure nedeniyle new T() ifadesi geçerli değildir. Compile-time hatası alırsınız çünkü generic türün runtime'da tür bilgisi silinir.
Tür silme nedeniyle, kodun derleme sırasında belirttiğiniz tür bilgisi çalışma zamanında mevcut değildir. Ancak, Generics sayesinde, belirli bir tür üzerinde işlem yapmak istediğinizde casting işlemleri otomatik olarak gerçekleşir. Bu durumu daha net anlamak için şu örneği inceleyelim:
java
Copy code
Box<String> stringBox = new Box<>();
stringBox.setValue("Merhaba");
String value = stringBox.getValue(); // Tür bilgisi silinmiş olsa da, otomatik olarak String'e dönüşüm gerçekleşir.
Burada stringBox.getValue() ifadesi, tür silmesine rağmen otomatik olarak String türüne dönüştürülür. Bu, Java'nın Generics mekanizmasının sağladığı bir avantajdır.
Ancak, eğer Box sınıfı daha genel bir türü temsil ediyorsa ve bu türün ne olduğunu bilemiyorsanız, o zaman değeri alırken elle (manual) casting yapmanız gerekebilir:
java
Copy code
Box<?> unknownBox = new Box<>();
Object rawValue = unknownBox.getValue(); // Tür bilgisi silindiği için rawValue tipi Object olur.
// Eğer ne tür olduğunu bilmiyorsanız ve kullanacaksanız, elle (manual) casting yapmanız gerekir.
Bu durumda, türünü bilmediğimiz için rawValueı kullanmadan önce elle casting yapmamız gerekebilir.
Tür silme (type erasure), Java Generics'inin çalışma zamanında tür bilgisini kaybetmesi durumunu ifade eder. Bu, derleme sırasında tür parametreleriyle ilgili bilgiye sahip olmamıza rağmen, bu bilginin çalışma zamanında kullanılamaz hale gelmesidir. İşte tür silmesi ile ilgili birkaç örnek:
Genel Bir Kutu Sınıfı:
java
Copy code
public class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
Yukarıdaki örnekte, Box sınıfı genel bir kutu temsil eder. Ancak, bu kutunun içindeki tür bilgisi çalışma zamanında kaybolur.
Tür Silmesi İle Çalışan Metot:
java
Copy code
public class Example {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setValue("Merhaba");
// Tür bilgisi silindiği halde, otomatik olarak String'e dönüşüm gerçekleşir.
String value = stringBox.getValue();
System.out.println(value);
}
}
Bu örnekte, Box sınıfı kullanılarak bir String değeri kutuya konulur ve daha sonra çıkarılır. Tür bilgisi silinmesine rağmen, otomatik olarak String türüne dönüşüm gerçekleşir.
Genel Olmayan (Raw) Tür Kullanımı:
java
Copy code
Box rawBox = new Box();
rawBox.setValue("Merhaba");
Object rawValue = rawBox.getValue();
// Elle (manual) casting yapmak gerekiyor.
String stringValue = (String) rawValue;
Box sınıfını genel bir tür parametresi olmadan kullanırsak, bu durumda tür bilgisi tamamen silinir. Değerleri kullanırken elle casting yapmamız gerekebilir.
Genel Metotlar ve Tür Silmesi:
java
Copy code
public class Example {
public static <T> T exampleMethod(Box<T> box) {
// Tür bilgisi silindiği için elle (manual) casting yapmak gerekiyor.
T value = box.getValue();
return value;
}
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setValue("Merhaba");
// Genel metodu çağırırken de tür bilgisi kaybolur.
String result = exampleMethod(stringBox);
System.out.println(result);
}
}
Burada, exampleMethod adlı genel bir metot kullanılmaktadır. Ancak, bu metot çağrıldığında, içindeki box parametresinin tür bilgisi silinir ve elle casting yapılması gerekebilir.
Joker Karakterleri (Wildcards) ve Tür Silmesi:
java
Copy code
public class Example {
public static void processBox(Box<?> box) {
// Tür bilgisi silindiği için sadece Object türünde kullanabiliriz.
Object value = box.getValue();
System.out.println(value);
}
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setValue("Merhaba");
// Joker karakterleri ile çalışırken sadece okuma işlemi yapabiliriz.
processBox(stringBox);
}
}
Bu örnekte, processBox metodu joker karakteri (?) kullanarak bir Box nesnesini parametre olarak alır. Ancak, joker karakteri kullanıldığında sadece okuma işlemi yapılabilir ve içindeki değer üzerinde değişiklik yapılamaz.