Menu
Coddy logo textTech

Wildcard'lar (?, extends, super)

Coddy'nin Java Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 57 / 87.

Sınırlı tür parametreleri, jenerikleri belirli türlerle kısıtlar; ancak mevcut jenerik türlerle çalışırken tam tür parametresini bilmediğinizde veya önemsemediğinizde ne olur? İşte burada joker karakterler (wildcards) devreye girer.

Sınırsız joker karakter (unbounded wildcard) ?, bilinmeyen bir türü temsil eder. Herhangi bir parametreli türü kabul etmek istediğinizde kullanışlıdır:

public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

printList(new ArrayList<String>());   // Çalışır
printList(new ArrayList<Integer>());  // Çalışır

Üstten sınırlı joker karakter (upper bounded wildcard) ? extends Type, belirtilen türü veya onun herhangi bir alt sınıfını kabul eder. Genel (generic) bir yapıdan veri okumanız gerektiğinde bunu kullanın:

public static double sumNumbers(List<? extends Number> numbers) {
    double sum = 0;
    for (Number n : numbers) {
        sum += n.doubleValue();
    }
    return sum;
}

sumNumbers(Arrays.asList(1, 2, 3));        // Integer Listesi
sumNumbers(Arrays.asList(1.5, 2.5));       // Double Listesi

Alt sınırlı joker karakter (lower bounded wildcard) ? super Type, belirtilen türü veya onun herhangi bir üst sınıfını kabul eder. Genel (generic) bir yapıya yazmanız gerektiğinde bunu kullanın:

public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

List<Number> numbers = new ArrayList<>();
addIntegers(numbers);  // Çalışır - Number, Integer'ın bir üst türüdür

Yardımcı bir kural: yalnızca okuma yaparken (üretici) extends kullanın, yalnızca yazma yaparken (tüketici) super kullanın. Bu, PECS prensibi olarak bilinir—Producer Extends, Consumer Super.

challenge icon

Görev

Kolay

Her üç joker karakter (wildcard) türünü de gösteren bir hayvanat bahçesi besleme sistemi kuralım! Hayvanlar ve yiyeceklerden oluşan bir hiyerarşi oluşturacak, ardından farklı besleme senaryolarını yönetmek için sınırsız (unbounded), üstten sınırlı (upper bounded) ve alttan sınırlı (lower bounded) joker karakterleri kullanan yardımcı metotlar yazacaksınız.

Kodunuzu dört dosya halinde düzenleyeceksiniz:

  • Food.java: Yiyecekler için basit bir sınıf hiyerarşisi oluşturun. Özel bir name (String) alanına, bunu başlatan bir kurucuya (constructor) ve bir getName() metoduna sahip bir Food temel sınıfı ile başlayın. Ardından aynı dosyada iki alt sınıf oluşturun: Meat, Food sınıfını genişletir (kurucu super(name) çağrısı yapar) ve Vegetable, Food sınıfını genişletir (aynı yapı).
  • Animal.java: Özel bir species (String) alanına, bir kurucuya ve bir getSpecies() metoduna sahip bir Animal temel sınıfı oluşturun. İki alt sınıf oluşturun: Her biri Animal sınıfını genişleten ve üst sınıf kurucusunu çağıran Carnivore ve Herbivore.
  • ZooFeeder.java: Joker karakterlerin parladığı yer burası! Üç statik metoda sahip bir yardımcı sınıf oluşturun:

    printInventory(List<?> items) - Herhangi bir listeyi yazdırmak için sınırsız bir joker karakter kullanır. Liste üzerinde döngü kurun ve her öğeyi toString() kullanarak her satıra bir tane gelecek şekilde yazdırın.

    calculateTotalFood(List<? extends Food> foods) - Herhangi bir Food türündeki listeden okuma yapmak için üstten sınırlı bir joker karakter kullanır. Öğelerin sayısını int olarak döndürün. Bu, bir üreticiden (producer) okuma yapmayı gösterir.

    addMeatToStock(List<? super Meat> stock, String meatName) - Meat tutabilen bir listeye yazma yapmak için alttan sınırlı bir joker karakter kullanır. Verilen isimle yeni bir Meat nesnesi oluşturun ve bunu stoğa ekleyin. Bu, bir tüketiciye (consumer) yazma yapmayı gösterir.

  • Main.java: Hayvanat bahçesi besleme sisteminizi bir araya getirin! İki girdi alacaksınız: bir et ismi (String) ve bir sebze ismi (String).

    İlk olarak, bir ArrayList<Food> oluşturun ve et ismiyle bir Meat nesnesi, sebze ismiyle bir Vegetable nesnesi ekleyin. Food inventory: yazdırın ve ardından bu liste ile printInventory metodunu çağırın.

    Ardından, boş bir satır yazdırın, sonra yemek listeniz üzerinde calculateTotalFood kullanarak Total food items: [count] yazdırın.

    Son olarak, meatStock adında yeni bir ArrayList<Food> oluşturun. Boş bir satır yazdırın, ardından Adding to meat stock... yazdırın. Bu liste ve "Beef" dizesi ile addMeatToStock metodunu çağırın. Sonra Meat stock after adding: yazdırın ve et stoğu üzerinde printInventory metodunu çağırın.

Sırasıyla iki girdi alacaksınız: bir et ismi ve bir sebze ismi.

Yazdırma işleminin doğru çalışması için Food sınıfınızda toString() metodunu yiyeceğin adını döndürecek şekilde geçersiz kılın (override). Her joker karakter türünün nasıl farklı bir amaca hizmet ettiğine dikkat edin: Sadece Object olarak okumanız gerektiğinde maksimum esneklik için ?, belirli türleri okurken ? extends ve bir koleksiyona yazarken ? super!

Kopya kağıdı

Java, tam tür parametresi bilinmediğinde veya ilgisiz olduğunda genel (generic) türlerle çalışmak için üç tür joker karakter (wildcard) sağlar.

Sınırsız Joker Karakter (?)

Bilinmeyen bir türü temsil eder ve herhangi bir parametreli türü kabul eder:

public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

printList(new ArrayList<String>());   // Çalışır
printList(new ArrayList<Integer>());  // Çalışır

Üst Sınırlı Joker Karakter (? extends Type)

Belirtilen türü veya onun alt sınıflarından herhangi birini kabul eder. Genel bir yapıdan veri okumanız gerektiğinde kullanın:

public static double sumNumbers(List<? extends Number> numbers) {
    double sum = 0;
    for (Number n : numbers) {
        sum += n.doubleValue();
    }
    return sum;
}

sumNumbers(Arrays.asList(1, 2, 3));        // Integer Listesi
sumNumbers(Arrays.asList(1.5, 2.5));       // Double Listesi

Alt Sınırlı Joker Karakter (? super Type)

Belirtilen türü veya onun üst sınıflarından herhangi birini kabul eder. Genel bir yapıya veri yazmanız gerektiğinde kullanın:

public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

List<Number> numbers = new ArrayList<>();
addIntegers(numbers);  // Çalışır - Number, Integer'ın bir üst türüdür

PECS Prensibi

Producer Extends, Consumer Super (Üretici Extends, Tüketici Super): Sadece okuma yaparken (üretici) extends, sadece yazma yaparken (tüketici) super kullanın.

Kendin dene

import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String meatName = scanner.nextLine();
        String vegetableName = scanner.nextLine();
        
        // TODO: Bir ArrayList<Food> oluşturun ve Meat ile Vegetable nesnelerini ekleyin
        
        // TODO: "Food inventory:" yazdırın, ardından printInventory fonksiyonunu çağırın
        
        // TODO: Boş bir satır yazdırın, ardından calculateTotalFood kullanarak "Total food items: [count]" yazdırın
        
        // TODO: meatStock adında yeni bir ArrayList<Food> oluşturun
        
        // TODO: Boş bir satır yazdırın, ardından "Adding to meat stock..." yazdırın
        
        // TODO: meatStock ve "Beef" ile addMeatToStock fonksiyonunu çağırın
        
        // TODO: "Meat stock after adding:" yazdırın, ardından meatStock üzerinde printInventory fonksiyonunu çağırın
    }
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler