1. Metodi di funzione

Se un'interfaccia ha un solo metodo , a una variabile di quel tipo di interfaccia può essere assegnato un valore dato da un'espressione lambda (funzione lambda). Tali interfacce sono diventate note come interfacce funzionali (dopo che Java ha aggiunto il supporto per le funzioni lambda).

Ad esempio, Java ha l' Consumer<Type>interfaccia, che ha il accept(Type obj)metodo. Perché è necessaria questa interfaccia?

In Java 8, le raccolte hanno un forEach()metodo che consente di eseguire alcune azioni per ogni elemento della raccolta . E qui l' Consumer<T>interfaccia funzionale viene utilizzata per passare l'azione al forEach()metodo.

Ecco come puoi visualizzare tutti gli elementi di una raccolta :

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach( (s) -> System.out.println(s) );
Visualizzazione di tutti gli elementi di una raccolta (utilizzando un'espressione lambda)

Il compilatore convertirà il codice precedente nel codice seguente:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s));
   }
});
Visualizzazione di tutti gli elementi di una raccolta (utilizzando una classe anonima)

La prima versione è decisamente più corta della seconda. E mentre il codice con espressioni lambda è difficile da leggere, il codice con classi interne anonime a volte è ancora più difficile da leggere.



2. Riferimento al metodo

Tuttavia, il nostro codice di espressione lambda può essere scritto ancora più breve.

Innanzitutto, puoi omettere le parentesi attorno al sparametro:

list.forEach( (s) -> System.out.println(s) );
Prima
list.forEach( s -> System.out.println(s) );
Dopo

Questo può essere fatto solo se c'è un parametro . Se sono presenti più parametri, è necessario utilizzare le parentesi .

E secondo, puoi scriverlo così:

list.forEach( System.out::println );
Notazione più compatta

Questa è la stessa identica notazione. Si noti che non ci sono parentesi dopo il println.

Qui abbiamo lo stesso codice — una chiamata al metodo:

object::method
x -> object.method(x)

Pensaci: volevamo compiere un'azione per ogni elemento della listcollezione. Se l'azione è una singola chiamata di funzione (come println()), allora ha senso passare semplicemente la funzione al metodo come parametro.

Ma come spieghiamo al compilatore che vogliamo passare il metodo piuttosto che chiamarlo? Per fare questo, invece dell'operatore punto, usiamo due due punti prima del nome del metodo. Un singolo colon è già utilizzato per indicare l'operatore ternario.

Questa è la notazione più semplice e compatta.



3. Costruttore

I riferimenti ai metodi con i due punti doppi sono molto utili quando lavoriamo con i flussi di I/O. Lo vedrai un po 'più tardi.

Nel frattempo, parliamo di 3 modi popolari per passare un riferimento al metodo:

Riferimento a un metodo di un oggetto

Per passare un riferimento a un metodo di un oggetto, devi scrivere qualcosa come . Questo codice è equivalente a .object::method
x -> object.method(x)

Le variabili speciali thise superpossono essere utilizzate come oggetto.

Riferimento a un metodo di una classe

Per passare un riferimento a un metodo statico, devi scrivere qualcosa come . Questo codice viene convertito in codice simileclass::methodx -> class.method(x);

Riferimento a un costruttore

Un costruttore si comporta in modo simile a un metodo di classe statico, quindi puoi anche passare un riferimento a un costruttore. Ecco come appare: .class::new

Ad esempio, puoi aggirare la cancellazione del tipo per le raccolte e passare al toArray()metodo un riferimento a un costruttore che creerà l'array desiderato:toArray(int[]::new);