Interfacce Funzionali Generiche in Java

Concetti Chiave
  • Le interfacce funzionali in Java possono essere generiche, permettendo di definire metodi che operano su tipi di dati specificati al momento della creazione dell'istanza.
  • Le espressioni lambda possono essere utilizzate con interfacce funzionali generiche, consentendo di scrivere codice più flessibile e riutilizzabile.
  • Le interfacce funzionali generiche permettono di definire metodi che accettano e restituiscono tipi di dati specifici, migliorando la leggibilità e la manutenibilità del codice.

Interfacce Funzionali Generiche

Un'espressione lambda, di per sé, non può specificare parametri di tipo. Pertanto, un'espressione lambda non può essere generica.

Naturalmente, a causa dell'inferenza di tipo, tutte le espressioni lambda mostrano alcune qualità simili ai tipi generici o generics.

Tuttavia, l'interfaccia funzionale associata a un'espressione lambda può essere generica. In questo caso, il tipo target dell'espressione lambda è determinato, in parte, dall'argomento di tipo o dagli argomenti specificati quando viene dichiarato un riferimento all'interfaccia funzionale.

Per comprendere il valore delle interfacce funzionali generiche, consideriamo questo. I due esempi nelle lezioni precedenti utilizzavano due interfacce funzionali diverse, una chiamata FunzioneNumerica e l'altra chiamata FunzioneStringa:

interface FunzioneNumerica {
    int funzione(int n);
}

interface FunzioneStringa {
    String funzione(String str);
}

Tuttavia, entrambe definivano un metodo chiamato funzione() che prendeva un parametro e restituiva un risultato. Nel primo caso, il tipo del parametro e il tipo di ritorno era int. Nel secondo caso, il parametro e il tipo di ritorno era String. Pertanto, l'unica differenza tra i due metodi era il tipo di dati che richiedevano. Invece di avere due interfacce funzionali i cui metodi differiscono solo nei loro tipi di dati, è possibile dichiarare un'interfaccia generica che possa essere utilizzata per gestire entrambe le circostanze.

Il seguente programma mostra questo approccio:

// Utilizzo di un'interfaccia funzionale generica con espressioni lambda.

// Un'interfaccia funzionale generica.
interface QualcheFunzione<T> {
    T funzione(T t);
}

// Una classe che dimostra l'uso di QualcheFunzione.
class DemoInterfacciaFunzionaleGenerica {

    public static void main(String[] args)
    {
        // Utilizza una versione basata su String di QualcheFunzione.
        QualcheFunzione<String> inverti = (str) -> {
            String risultato = "";
            int i;
            for(i = str.length()-1; i >= 0; i--)
                risultato += str.charAt(i);
            return risultato;
        };

        // Ora, usa l'interfaccia funzionale generica
        // per invertire le stringhe.
        System.out.println("Lambda invertita è " +
                           inverti.funzione("Lambda"));
        System.out.println("Espressione invertita è " +
                           inverti.funzione("Espressione"));

        // Ora, utilizza una versione basata su Integer di QualcheFunzione.
        QualcheFunzione<Integer> fattoriale = (n) -> {
            int risultato = 1;
            for(int i=1; i <= n; i++)
                risultato = i * risultato;
            return risultato;
        };

        // Ora, usa l'interfaccia funzionale generica
        // per calcolare il fattoriale di due numeri.
        System.out.println("Il fattoriale di 3 è " +
                           fattoriale.funzione(3));
        System.out.println("Il fattoriale di 5 è " +
                           fattoriale.funzione(5));
    }

}

L'output è mostrato qui:

Lambda invertita è adbmaL
Espressione invertita è enoisserpsE
Il fattoriale di 3 è 6
Il fattoriale di 5 è 120

Nel programma, l'interfaccia funzionale generica QualcheFunzione è dichiarata come mostrato qui:

interface QualcheFunzione<T> {
    T funzione(T t);
}

Qui, T specifica sia il tipo di ritorno che il tipo di parametro di funzione(). Questo significa che è compatibile con qualsiasi espressione lambda che prende un parametro e restituisce un valore dello stesso tipo.

L'interfaccia QualcheFunzione è utilizzata per fornire un riferimento a due diversi tipi di lambda. Il primo utilizza il tipo String. Il secondo utilizza il tipo Integer. Pertanto, la stessa interfaccia funzionale può essere utilizzata per riferirsi al lambda inverti e al lambda fattoriale. Solo l'argomento di tipo passato a QualcheFunzione differisce.