1. Interfacce Funzionali
Un'interfaccia funzionale è un'interfaccia che contiene un solo metodo astratto.
Queste interfacce possono essere implementate utilizzando le espressioni lambda o i riferimenti ai metodi.
Tipi Principali di Interfacce Funzionali
- Consumer
L'interfaccia
Consumer
accetta un argomento e non restituisce alcun risultato.
import java.util.function.Consumer; public class ConsumerEsempio { public static void main(String[] args) { Consumer<String> stampa = s -> System.out.println(s); stampa.accept("Hello, Consumer!"); } }
- Supplier
L'interfaccia
Supplier
non accetta argomenti ma restituisce un valore.
import java.util.function.Supplier; public class SupplierEsempio { public static void main(String[] args) { Supplier<Double> random = () -> Math.random(); System.out.println(random.get()); } }
- Predicate
L'interfaccia
Predicate
accetta un argomento e restituisce un valore booleano.
import java.util.function.Predicate; public class PredicateEsempio { public static void main(String[] args) { Predicate<Integer> isEven = n -> n % 2 == 0; System.out.println(isEven.test(4)); // Output: true System.out.println(isEven.test(5)); // Output: false } }
- Function
L'interfaccia
Function
accetta un argomento e restituisce un valore. Può essere utilizzata per trasformare i dati.
import java.util.function.Function; public class FunctionEsempio { public static void main(String[] args) { Function<String, Integer> lunghezza = s -> s.length(); System.out.println(lunghezza.apply("Java")); // Output: 4 } }
Composizione di Funzioni
Le interfacce
Function
e Predicate
supportano metodi di composizione come andThen()
e compose()
, che consentono di concatenare più operazioni.- andThen()
Function<Integer, Integer> raddoppia = n -> n * 2; Function<Integer, Integer> aggiungiTre = n -> n + 3; Function<Integer, Integer> combinata = raddoppia.andThen(aggiungiTre); System.out.println(combinata.apply(5)); // Output: 13
- compose()
Function<Integer, Integer> combinata2 = raddoppia.compose(aggiungiTre); System.out.println(combinata2.apply(5)); // Output: 16
2. Optional
La classe
Optional
è stata introdotta in Java 8 per rappresentare un valore che potrebbe essere presente o assente, evitando il rischio di NullPointerException
.Creazione di Optional
empty()
: Crea un Optional vuoto.
Optional<String> vuoto = Optional.empty();
of()
: Crea un Optional con un valore non nullo.
Optional<String> presente = Optional.of("Java");
ofNullable()
: Crea un Optional che può contenere un valore nullo.
Optional<String> opzionale = Optional.ofNullable(null);
Metodi Principali di Optional
isPresent()
: Verifica se il valore è presente.
if (opzionale.isPresent()) { System.out.println(opzionale.get()); }
ifPresent()
: Esegue un'operazione se il valore è presente.
opzionale.ifPresent(val -> System.out.println(val));
orElse()
: Restituisce un valore di default se il valore non è presente.
String valore = opzionale.orElse("Default");
orElseGet()
: Restituisce un valore generato da unSupplier
se il valore non è presente.
String valore = opzionale.orElseGet(() -> "Generato");
orElseThrow()
: Lancia un'eccezione se il valore non è presente.
String valore = opzionale.orElseThrow(() -> new IllegalStateException("Valore mancante"));
Optional e Stream
Optional
si integra bene con le Stream API, consentendo di gestire valori opzionali in modo funzionale.Esempio:
import java.util.Optional; import java.util.stream.Stream; public class OptionalStream { public static void main(String[] args) { Optional<String> opzionale = Stream.of("Java", "Python", "C++") .filter(s -> s.startsWith("J")) .findFirst(); opzionale.ifPresent(System.out::println); // Output: Java } }
3. Relazione tra Interfacce Funzionali e Stream
Le Stream API si basano ampiamente sulle interfacce funzionali:
- Consumer e forEach:
List<String> nomi = Arrays.asList("Anna", "Luca", "Marco"); nomi.stream().forEach(System.out::println);
- Predicate e filter:
List<String> filtrati = nomi.stream() .filter(nome -> nome.startsWith("M")) .collect(Collectors.toList()); System.out.println(filtrati); // Output: [Marco]
- Function e map:
List<Integer> lunghezze = nomi.stream() .map(String::length) .collect(Collectors.toList()); System.out.println(lunghezze); // Output: [4, 4, 5]
- Supplier e Optional:
Optional<String> valore = Optional.of("Java"); System.out.println(valore.orElseGet(() -> "Default")); // Output: Java