0Pricing
Java Academy · レッスン

Optional のベストプラクティス

orElse、orElseGet、orElseThrow を適切に使い、よくある Optional のアンチパターンを避けます。

「Optional のベストプラクティス」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

Optionalのベストプラクティス

Optionalは正しく使用すれば強力なツールです。このレッスンでは、実際のコードベースの具体例を通して、行うべきことと避けるべきことを説明します。

行うべきこと: 戻り値の型としてOptionalを使用する

Optionalの主な用途は、値を見つけられない、または生成できない可能性があるメソッドの戻り値の型として使用することです。

import java.util.Optional;

// Good: signals to caller that result might be absent
public Optional<User> findByUsername(String username) { ... }
public Optional<Config> getConfig(String key) { ... }
public Optional<String> getHeader(String name) { ... }

// These are all cases where absence is a normal outcome,
// not an exceptional condition

避けるべきこと: フィールドにOptionalを使用する

Optionalのフィールドはオブジェクトのオーバーヘッドを増やし、シリアライズを複雑にします。クラス内部の省略可能なフィールドには、nullまたは番兵値を使用してください。

// BAD
class User {
    private Optional<String> middleName; // wrong
}

// GOOD: use null for absent optional field
class User {
    private final String middleName; // null = absent
    public Optional<String> middleName() {
        return Optional.ofNullable(middleName); // expose as Optional in getter
    }
}
User u = new User(null); // null field
System.out.println(u.middleName().isPresent()); // false

避けるべきこと: メソッドの引数にOptionalを使用する

Optionalの引数を使うと、呼び出し側に値を不要なOptionalでラップさせることになります。オーバーロードまたは@Nullableを使用してください。

// BAD
void sendNotification(String message, Optional<String> subject) { ... }
// caller must write: sendNotification("Hello", Optional.of("News"))

// GOOD: overloading
void sendNotification(String message) { sendNotification(message, null); }
void sendNotification(String message, String subject) {
    // subject may be null
}
// caller writes: sendNotification("Hello", "News") — clean

避けるべきこと: コレクションにOptionalを使用する

List、Set、またはMapの値としてOptionalを格納しないでください。コレクション内の空のOptionalには意味がないため、そのエントリー自体を省略してください。

// BAD: List of Optionals
List<Optional<User>> results = ...; // meaningless empty Optionals

// GOOD: List of present values only
List<User> presentUsers = maybeUsers.stream()
    .flatMap(Optional::stream)
    .collect(Collectors.toList());

// BAD: Map with Optional values
Map<String, Optional<String>> config = ...;

// GOOD: just use null or omit the key for absent values
Map<String, String> config2 = ...;
String value = config2.getOrDefault("key", "default");

行うべきこと: 計算コストの高いデフォルト値にはorElseGetを使用する

デフォルト値の生成に計算(DBクエリやネットワーク呼び出し)が必要な場合は、必ずorElseGetを使用してください。サプライヤーは必要な場合にのみ呼び出されます。

import java.util.Optional;

// BAD: buildDefault() always called, even when value is present!
String config = getConfig("timeout").orElse(buildDefault());

// GOOD: supplier called lazily only when empty
String config2 = getConfig("timeout").orElseGet(() -> buildDefault());

// Or with method reference:
String config3 = getConfig("timeout").orElseGet(this::buildDefault);

// For cheap literals, orElse is fine
String name = getConfig("name").orElse("default");

行うべきこと: 必須の値にはorElseThrowを使用する

値がないことがプログラミングエラーまたは不変条件違反を示す場合は、説明的な例外とともにorElseThrowを使用してください。

import java.util.Optional;

// Good: throw when absence is an error
User user = userRepository.findById(userId)
    .orElseThrow(() -> new UserNotFoundException("User not found: " + userId));

// Good: in validation code
String apiKey = Optional.ofNullable(System.getenv("API_KEY"))
    .orElseThrow(() -> new IllegalStateException(
        "API_KEY environment variable is required"));

System.out.println("Key found: " + apiKey.length() + " chars");

アンチパターン: isPresent + get()

isPresent()を確認してからget()を呼び出すアンチパターンは避けてください。これはnullチェックと変わらず、Optionalの目的を損ないます。

import java.util.Optional;

Optional<String> opt = findValue();

// BAD: isPresent() + get() — no improvement over null check
if (opt.isPresent()) {
    String val = opt.get();
    process(val);
}

// GOOD: use the functional API
opt.ifPresent(this::process);

// GOOD: transform and return
return opt.map(this::process).orElse(null);

// GOOD: provide a default
String result = opt.orElse("default");

連結のベストプラクティス

find → filter → transform → fallbackという、明快な連結を構築してください。各ステップが自己文書化されます。

import java.util.Optional;

record User(long id, String plan, boolean active) {}

static Optional<User> findUser(long id) { return Optional.empty(); }

String result = findUser(42L)
    .filter(User::active)           // keep only active users
    .filter(u -> "PRO".equals(u.plan())) // keep only PRO users
    .map(u -> "Welcome, Pro user #" + u.id()) // transform
    .orElse("Upgrade to Pro!");     // fallback

System.out.println(result); // Upgrade to Pro!

サービス層でのOptional

ベストプラクティスに従った、現実的なサービス層の例です。

import java.util.Optional;

class OrderService {
    public Optional<OrderSummary> getOrderSummary(long orderId) {
        return orderRepo.findById(orderId)
            .filter(o -> !o.isCancelled())
            .map(this::toSummary);
    }

    public OrderSummary requireOrderSummary(long orderId) {
        return getOrderSummary(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
    }

    private OrderSummary toSummary(Order o) { return new OrderSummary(o); }
}
// API callers choose: Optional (handle absence) or require (exception)

Optionalとnull安全性のツール

Optionalは、@Nullable/@NonNullアノテーションやSpotBugs/NullAwayなどのツールを補完し、包括的なnull安全性を実現します。

// Annotation-based: declares intent for in-class nulls
import org.jetbrains.annotations.*;

class ProductService {
    @Nullable  // may return null — for framework/bean use
    public String getCachedName(String id) { return cache.get(id); }

    // Optional — for service API that handles absence
    public Optional<Product> findProduct(String id) {
        return repo.findById(id);
    }
}
// Use @Nullable for fields/parameters; Optional for return types

クイックチェック

次のコードの問題点は何ですか。

if (opt.isPresent()) {
    process(opt.get());
}

まとめ: Optionalのベストプラクティス

要点:

  • Optionalは戻り値の型としてのみ使用し、フィールドやメソッドの引数には使用しないでください
  • デフォルト値の計算にコストがかかる場合は、orElseよりorElseGetを優先してください
  • 値がないことがプログラミングエラーを表す場合は、orElseThrowを使用してください
  • isPresent() + get()は避け、ifPresent、map、またはorElseを使用してください
  • Optionalをコレクションに格納せず、ストリームをフィルタリングしてください
  • filter → map → orElseという読みやすい連結を構築し、データの流れを明確にしてください

よくある質問

「Optional のベストプラクティス」レッスンは無料ですか?

はい。「Optional のベストプラクティス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「Optional のベストプラクティス」で何を学びますか?

orElse、orElseGet、orElseThrow を適切に使い、よくある Optional のアンチパターンを避けます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「Optional のベストプラクティス」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Optional が存在する理由
  2. Optional の作成と確認
  3. Optional の変換:map と flatMap
  4. Optional のベストプラクティス
← Java Academyに戻る