Interfacce Funzionali e Optional

Tags
Published
Author

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

  1. Consumer L'interfaccia Consumer accetta un argomento e non restituisce alcun risultato.
    1. 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!"); } }
  1. Supplier L'interfaccia Supplier non accetta argomenti ma restituisce un valore.
    1. import java.util.function.Supplier; public class SupplierEsempio { public static void main(String[] args) { Supplier<Double> random = () -> Math.random(); System.out.println(random.get()); } }
  1. Predicate L'interfaccia Predicate accetta un argomento e restituisce un valore booleano.
    1. 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 } }
  1. Function L'interfaccia Function accetta un argomento e restituisce un valore. Può essere utilizzata per trasformare i dati.
    1. 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.
  1. andThen()
    1. 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
  1. compose()
    1. 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

  1. empty(): Crea un Optional vuoto.
    1. Optional<String> vuoto = Optional.empty();
  1. of(): Crea un Optional con un valore non nullo.
    1. Optional<String> presente = Optional.of("Java");
  1. ofNullable(): Crea un Optional che può contenere un valore nullo.
    1. Optional<String> opzionale = Optional.ofNullable(null);

Metodi Principali di Optional

  1. isPresent(): Verifica se il valore è presente.
    1. if (opzionale.isPresent()) { System.out.println(opzionale.get()); }
  1. ifPresent(): Esegue un'operazione se il valore è presente.
    1. opzionale.ifPresent(val -> System.out.println(val));
  1. orElse(): Restituisce un valore di default se il valore non è presente.
    1. String valore = opzionale.orElse("Default");
  1. orElseGet(): Restituisce un valore generato da un Supplier se il valore non è presente.
    1. String valore = opzionale.orElseGet(() -> "Generato");
  1. orElseThrow(): Lancia un'eccezione se il valore non è presente.
    1. 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:
  1. Consumer e forEach:
    1. List<String> nomi = Arrays.asList("Anna", "Luca", "Marco"); nomi.stream().forEach(System.out::println);
  1. Predicate e filter:
    1. List<String> filtrati = nomi.stream() .filter(nome -> nome.startsWith("M")) .collect(Collectors.toList()); System.out.println(filtrati); // Output: [Marco]
  1. Function e map:
    1. List<Integer> lunghezze = nomi.stream() .map(String::length) .collect(Collectors.toList()); System.out.println(lunghezze); // Output: [4, 4, 5]
  1. Supplier e Optional:
    1. Optional<String> valore = Optional.of("Java"); System.out.println(valore.orElseGet(() -> "Default")); // Output: Java

Back to →
🐚
Java