Optional の変換:map と flatMap
map と flatMap で変換を連鎖させ、null チェックなしで値を処理します。
「Optional の変換:map と flatMap」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
Optionalの変換: mapとflatMap
map()は、値が存在する場合にOptional内の値を変換します。flatMap()は、変換処理自体がOptionalを返す場合に、Optionalの入れ子を防ぎます。
map: 値の変換
map(Function)は、値が存在する場合にその値へ関数を適用し、結果を新しいOptionalでラップします。元の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()); // falsemap呼び出しの連結
複数のmap呼び出しを連結すると、入れ子になった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 YORKflatMap: Optional<Optional>の回避
マッピング関数がOptionalを返す場合は、flatMapを使用して、入れ子になったOptional<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実践的なflatMap: リポジトリーの連結
よくあるパターンとして、各処理が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.commapとflatMapのまとめ
mapとflatMapの使い分け:
- 関数が通常の値(String、Integer)を返す場合は
mapを使用します - 関数がOptionalを返す場合は
flatMapを使用します
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>>)flatMapを使った安全な解析
便利なパターンとして、失敗する可能性のある危険な処理を、flatMapを使ってOptionalでラップします。
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)); // -1Optionalの収集
Optionalのストリームをフィルタリングしてmapを適用し、値が存在するものだけを収集します。
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]入れ子になったオブジェクトの走査
mapとflatMapを使うと、煩雑な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
mapはOptionalの値の型変換に適しています。たとえば、解析、フォーマット、サブフィールドの抽出などに使えます。
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パイプラインの構築
filter、map、flatMapを組み合わせると、明示的な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クイックチェック
Optionalに対してmapではなくflatMapを使用するのは、どのような場合ですか。
まとめ: Optionalの変換: mapとflatMap
要点:
- map()は値が存在する場合に変換し、結果をOptionalでラップします
- flatMap()はOptionalを返す関数に使用し、Optionalの入れ子を防ぎます
- 複数のmapを連結すると、nullチェックなしで複数段階の変換を実行できます
- Optionalを返すリポジトリーやサービスの呼び出しを連結するにはflatMapを使用します
- filter + map + flatMapを組み合わせて、null安全なデータパイプラインを構築します
- flatMap Optional::streamを使うと、Optionalをストリームパイプラインに組み込めます
よくある質問
「Optional の変換:map と flatMap」レッスンは無料ですか?
はい。「Optional の変換:map と flatMap」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「Optional の変換:map と flatMap」で何を学びますか?
map と flatMap で変換を連鎖させ、null チェックなしで値を処理します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「Optional の変換:map と flatMap」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Optional が存在する理由
- Optional の作成と確認
- Optional の変換:map と flatMap
- Optional のベストプラクティス