0Pricing
Java Academy · Lekcja

Przekształcanie obiektów Optional: map i flatMap

Łącz przekształcenia za pomocą map i flatMap, aby przetwarzać wartości bez sprawdzania null.

Przekształcanie obiektów Optional: map i flatMap to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Przekształcanie Optional: map i flatMap

map() przekształca wartość wewnątrz Optional, jeśli jest ona obecna. flatMap() zapobiega zagnieżdżaniu Optional, gdy samo przekształcenie zwraca Optional.

map: przekształcanie wartości

map(Function) stosuje funkcję do wartości, jeśli jest ona obecna, i opakowuje wynik w nowy Optional. Jeśli pierwotny Optional był pusty, metoda zwraca pusty Optional.

import java.util.Optional;

Optional<String> name = Optional.of("alice");

Optional<String> upper = name.map(String::toUpperCase);
System.out.println(upper.get()); // ALICE

Optional<Integer> length = name.map(String::length);
System.out.println(length.get()); // 5

// Empty in → empty out
Optional<String> empty = Optional.<String>empty().map(String::toUpperCase);
System.out.println(empty.isPresent()); // false

Łączenie wywołań map

Wiele wywołań map pozwala przejrzyście łączyć przekształcenia bez zagnieżdżonych sprawdzeń null.

import java.util.Optional;

record Address(String city, String country) {}
record User(String name, Address address) {}

User user = new User("Alice", new Address("New York", "US"));

// Chain of maps — safe even if any step returns null
Optional<String> city = Optional.ofNullable(user)
    .map(User::address)
    .map(Address::city)
    .map(String::toUpperCase);

System.out.println(city.orElse("Unknown")); // NEW YORK

flatMap: unikanie Optional<Optional>

Jeśli funkcja mapująca zwraca Optional, należy użyć flatMap, aby zachować płaski Optional zamiast zagnieżdżonego Optional<Optional>.

import java.util.Optional;

record User(String name, Optional<String> email) {}

User u = new User("Bob", Optional.of("bob@example.com"));

// map returns Optional<Optional<String>> — ugly!
Optional<Optional<String>> nested = Optional.of(u).map(User::email);

// flatMap flattens: Optional<String>
Optional<String> flat = Optional.of(u).flatMap(User::email);
System.out.println(flat.get()); // bob@example.com

Rzeczywiste użycie flatMap: łańcuch repozytoriów

Częsty wzorzec: łączenie wywołań repozytoriów, z których każde zwraca Optional.

import java.util.Optional;

record Order(long id, long userId, double total) {}
record User(long id, String name, String email) {}

static Optional<Order> findOrder(long orderId) {
    return orderId == 1 ? Optional.of(new Order(1, 42, 99.99)) : Optional.empty();
}
static Optional<User> findUser(long userId) {
    return userId == 42 ? Optional.of(new User(42, "Alice", "a@co.com")) : Optional.empty();
}

Optional<String> email = findOrder(1)
    .flatMap(o -> findUser(o.userId()))
    .map(User::email);

System.out.println(email.orElse("Not found")); // a@co.com

Podsumowanie map i flatMap

Wybór między map a flatMap:

  • Należy używać map, gdy funkcja zwraca zwykłą wartość (String, Integer)
  • Należy używać flatMap, gdy funkcja zwraca Optional
import java.util.Optional;

// map: T → R
Optional.of("hello").map(String::toUpperCase);       // Optional<String>
Optional.of("42").map(Integer::parseInt);             // Optional<Integer>

// flatMap: T → Optional<R>
Optional.of("42").flatMap(s -> {
    try { return Optional.of(Integer.parseInt(s)); }
    catch (NumberFormatException e) { return Optional.empty(); }
}); // Optional<Integer> (not Optional<Optional<Integer>>)

Bezpieczne parsowanie za pomocą flatMap

Przydatny wzorzec: opakowanie w Optional operacji, które mogą zakończyć się błędem, za pomocą flatMap.

import java.util.Optional;

static Optional<Integer> tryParseInt(String s) {
    try { return Optional.of(Integer.parseInt(s)); }
    catch (NumberFormatException e) { return Optional.empty(); }
}

Optional<String> input = Optional.of("123");
Optional<Integer> parsed = input.flatMap(JavaOptional::tryParseInt);
System.out.println(parsed.map(n -> n * 2).orElse(-1)); // 246

Optional<String> bad = Optional.of("abc");
Optional<Integer> failed = bad.flatMap(JavaOptional::tryParseInt);
System.out.println(failed.orElse(-1)); // -1

Zbieranie wartości Optional

Funkcje filter i map zastosowane do strumienia Optional pozwalają zebrać tylko obecne wartości.

import java.util.*;
import java.util.stream.*;

List<String> inputs = List.of("10", "abc", "25", "nope", "7");

List<Integer> numbers = inputs.stream()
    .flatMap(s -> {
        try { return Stream.of(Integer.parseInt(s)); }
        catch (NumberFormatException e) { return Stream.empty(); }
    })
    .collect(Collectors.toList());

System.out.println(numbers); // [10, 25, 7]

Przechodzenie przez zagnieżdżone obiekty

map i flatMap umożliwiają bezpieczne przechodzenie przez głębokie grafy obiektów bez żmudnych sprawdzeń null.

import java.util.Optional;

record Company(String name, Optional<Address> address) {}
record Address(String city, Optional<String> zipCode) {}

Company company = new Company(
    "Acme Corp",
    Optional.of(new Address("Austin", Optional.of("78701")))
);

String zip = Optional.of(company)
    .flatMap(Company::address)
    .flatMap(Address::zipCode)
    .orElse("N/A");

System.out.println(zip); // 78701

map do konwersji typów

map świetnie nadaje się do konwertowania typów wartości Optional — na przykład podczas parsowania, formatowania lub wyodrębniania pola podrzędnego.

import java.util.Optional;
import java.time.LocalDate;

// Parse a date from String safely
static Optional<LocalDate> parseDate(String s) {
    try { return Optional.of(LocalDate.parse(s)); }
    catch (Exception e) { return Optional.empty(); }
}

Optional<String> dateStr = Optional.of("2024-06-15");
Optional<Integer> year = dateStr
    .flatMap(JavaOptional::parseDate)
    .map(LocalDate::getYear);

System.out.println(year.orElse(-1)); // 2024

Budowanie potoków

Połączenie filter, map i flatMap pozwala budować wyraziste potoki danych, które obsługują brak wartości na każdym etapie bez jawnych sprawdzeń null.

import java.util.Optional;

record Product(String sku, String category, boolean inStock, double price) {}

static Optional<Product> findProduct(String sku) {
    if ("LAPTOP-1".equals(sku))
        return Optional.of(new Product("LAPTOP-1", "Electronics", true, 999.99));
    return Optional.empty();
}

Optional<String> priceTag = findProduct("LAPTOP-1")
    .filter(Product::inStock)
    .filter(p -> p.price() < 2000)
    .map(p -> String.format("%s: $%.2f", p.sku(), p.price()));

System.out.println(priceTag.orElse("Not available")); // LAPTOP-1: $999.99

Szybkie sprawdzenie

Kiedy należy użyć flatMap zamiast map dla Optional?

Podsumowanie: przekształcanie Optional: map i flatMap

Najważniejsze informacje:

  • map() przekształca obecną wartość, opakowując wynik w Optional
  • flatMap() służy do funkcji zwracających Optional i zapobiega zagnieżdżaniu Optional
  • Wiele wywołań map można łączyć w wieloetapowe przekształcenia bez sprawdzeń null
  • flatMap należy stosować do łączenia wywołań repozytoriów i usług zwracających Optional
  • Połączenie filter + map + flatMap pozwala budować bezpieczne względem null potoki danych
  • flatMap Optional::stream pozwala włączać Optional do potoków strumieni

Często zadawane pytania

Czy lekcja „Przekształcanie obiektów Optional: map i flatMap” jest bezpłatna?

Tak — pełny tekst „Przekształcanie obiektów Optional: map i flatMap” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Przekształcanie obiektów Optional: map i flatMap”?

Łącz przekształcenia za pomocą map i flatMap, aby przetwarzać wartości bez sprawdzania null. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Przekształcanie obiektów Optional: map i flatMap”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego istnieje Optional
  2. Tworzenie i sprawdzanie obiektów Optional
  3. Przekształcanie obiektów Optional: map i flatMap
  4. Dobre praktyki używania Optional
← Powrót do Java Academy