Menu
Coddy logo textTech

Caratteri jolly (?, extends, super)

Fa parte della sezione Programmazione orientata agli oggetti del percorso Java di Coddy. Lezione 57 di 87.

I parametri di tipo limitati restringono i tipi generici a tipi specifici, ma cosa succede quando lavori con tipi generici esistenti e non conosci, o non ti interessa, il parametro di tipo esatto? È qui che entrano in gioco i wildcard.

Il wildcard illimitato ? rappresenta un tipo sconosciuto. È utile quando vuoi accettare qualsiasi tipo parametrizzato:

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

printList(new ArrayList<String>());   // Funziona
printList(new ArrayList<Integer>());  // Funziona

Il carattere jolly con limite superiore ? extends Type accetta il tipo specificato o una qualsiasi delle sue sottoclassi. Usalo quando devi leggere da una struttura generica:

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));        // List di Integer
sumNumbers(Arrays.asList(1.5, 2.5));       // List di Double

Il wildcard con limite inferiore ? super Type accetta il tipo specificato o una qualsiasi delle sue superclassi. Usalo quando devi scrivere in una struttura generica:

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

List<Number> numbers = new ArrayList<>();
addIntegers(numbers);  // Funziona: Number è un supertipo di Integer

Una linea guida utile: usa extends quando leggi soltanto (produttore), usa super quando scrivi soltanto (consumatore). Questo è noto come principio PECS: Producer Extends, Consumer Super.

challenge icon

Sfida

Facile

Costruiamo un sistema di alimentazione dello zoo che dimostri tutti e tre i tipi di caratteri jolly! Creerai una gerarchia di animali e alimenti, poi scriverai metodi di utilità che usano caratteri jolly senza limiti, con limite superiore e con limite inferiore per gestire diversi scenari di alimentazione.

Organizzerai il codice in quattro file:

  • Food.java: Crea una semplice gerarchia di classi per gli alimenti. Inizia con una classe base Food che ha un campo privato name (String), un costruttore per inizializzarlo e un metodo getName(). Poi crea due sottoclassi nello stesso file: Meat estende Food (il costruttore chiama super(name)) e Vegetable estende Food (stesso schema).
  • Animal.java: Crea una classe base Animal con un campo privato species (String), un costruttore e un metodo getSpecies(). Crea due sottoclassi: Carnivore e Herbivore, ognuna delle quali estende Animal e chiama il costruttore della classe genitore.
  • ZooFeeder.java: È qui che i caratteri jolly danno il meglio! Crea una classe di utilità con tre metodi statici:

    printInventory(List<?> items) - Usa un carattere jolly senza limiti per stampare qualsiasi lista. Scorri la lista e stampa ogni elemento usando toString(), uno per riga.

    calculateTotalFood(List<? extends Food> foods) - Usa un carattere jolly con limite superiore per leggere da una lista di qualsiasi tipo di Food. Restituisci il conteggio degli elementi come int. Questo dimostra la lettura da un produttore.

    addMeatToStock(List<? super Meat> stock, String meatName) - Usa un carattere jolly con limite inferiore per scrivere in una lista che può contenere Meat. Crea un nuovo oggetto Meat con il nome specificato e aggiungilo alle scorte. Questo dimostra la scrittura in un consumatore.

  • Main.java: Metti insieme il tuo sistema di alimentazione dello zoo! Riceverai due input: un nome di carne (String) e un nome di verdura (String).

    Per prima cosa, crea un ArrayList<Food> e aggiungi un oggetto Meat con il nome della carne e un oggetto Vegetable con il nome della verdura. Stampa Food inventory:, poi chiama printInventory con questa lista.

    Poi, stampa una riga vuota e quindi stampa Total food items: [count] usando calculateTotalFood sulla tua lista di alimenti.

    Infine, crea un nuovo ArrayList<Food> chiamato meatStock. Stampa una riga vuota e poi Adding to meat stock.... Chiama addMeatToStock con questa lista e la stringa "Beef". Poi stampa Meat stock after adding: e chiama printInventory sulle scorte di carne.

Riceverai due input nell'ordine seguente: un nome di carne e un nome di verdura.

Sovrascrivi toString() nella tua classe Food affinché restituisca il nome dell'alimento, così la stampa funziona correttamente. Nota come ogni tipo di carattere jolly abbia uno scopo diverso: ? per la massima flessibilità quando devi solo leggere come Object, ? extends quando leggi tipi specifici e ? super quando scrivi in una collezione!

Provalo tu

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: Crea un ArrayList<Food> e aggiungi oggetti Meat e Vegetable
        
        // TODO: Stampa "Food inventory:", poi chiama printInventory
        
        // TODO: Stampa una riga vuota, poi "Total food items: [count]" usando calculateTotalFood
        
        // TODO: Crea un nuovo ArrayList<Food> chiamato meatStock
        
        // TODO: Stampa una riga vuota, poi "Adding to meat stock..."
        
        // TODO: Chiama addMeatToStock con meatStock e "Beef"
        
        // TODO: Stampa "Meat stock after adding:", poi chiama printInventory su meatStock
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Java online