メインコンテンツまでスキップ

Java Optional Best Practices

· 約6分
Mikyan
白い柴犬

JavaのOptionalは、値が存在する場合も存在しない場合もあるコンテナオブジェクトです。これにより、nullチェックの手間を減らし、メソッドの戻り値として「値があるかどうか」を明示的に伝えることができます。Optionalは以下のような利点があります:

明確な意図: メソッドが値を返さない可能性があることを示す。 安全な操作: 明示的なnullチェックを回避し、NullPointerExceptionのリスクを減少させる。 関数型スタイル: map, filter, flatMap などを活用したチェーン処理が可能。

Optionalの作成

Optionalは主に2つのファクトリーメソッドを使って生成します。

  • Optional.of(T value):

    • 値が必ず非nullであることを期待します。
    • nullを渡すと、NullPointerExceptionが発生します。
  • Optional.ofNullable(T value):

    • 値がnullの場合は空のOptional(Optional.empty())を返し、非nullの場合は値を保持します。

作成例:

// Method that processes an input string and returns an Optional.
public static Optional<String> processValue(String input) {
// Return empty if input is null or blank
if (input == null || input.trim().isEmpty()) {
return Optional.empty();
}
// Otherwise, return a trimmed value wrapped in an Optional
return Optional.of(input.trim());
}

Optionalの利用方法

Optionalの利用は主に以下のユースケースに分類されます。

  1. 値の存在チェック
    • isPresent() / isEmpty():
      • 値が存在するかどうかを確認する。
      • 例: if (optional.isPresent())
  2. 値の取得
    • get():
      • 値が存在する場合に返すが、存在しない場合はNoSuchElementExceptionをスローします。
      • 注意: 直接get()を使うのはリスクがあるため、他のメソッドと組み合わせることが望ましい。
    • orElse(defaultValue):
      • 値が存在しない場合にデフォルト値を返します。
    • orElseThrow(exceptionSupplier):
      • Optionalが空の場合に、指定した例外をスローします。
  3. 値の処理
    • ifPresent(Consumer):
      • 値が存在する場合に、Consumer(値を受け取って何らかの処理を行うラムダ式)を実行します。
    • ifPresentOrElse(Consumer, Runnable):
      • 値が存在する場合はConsumerを、存在しない場合はRunnable(引数を取らない処理)を実行します。
  4. Optionalのチェーン処理
    • map(Function):
      • 値が存在する場合に、Functionを適用し結果を新しいOptionalとして返します。
    • filter(Predicate):
      • 値が条件を満たすかどうかをチェックし、満たさない場合は空のOptionalを返します。
    • flatMap(Function):
      • ネストされたOptionalの解除に利用します。
    • or(Supplier):
      • Optionalが空の場合に、新たなOptionalを返すために利用します。

利用例:

String value1 = "  Hello, World!  ";
Optional<String> opt1 = processValue(value1);

// 存在チェック
if (opt1.isPresent()) {
System.out.println("Value exists!");
} else {
System.out.println("Value is empty.");
}

// 値の取得
try {
// 値がなければ例外がスローされる
String result = opt1.orElseThrow(() -> new IllegalStateException("Value is empty"));
System.out.println("Result: " + result);
} catch (IllegalStateException ex) {
System.err.println(ex.getMessage());
}

// デフォルト値を利用
String defaultResult = opt1.orElse("Default Value");
System.out.println("Default Result: " + defaultResult);

// 値の処理
opt1.ifPresent(val -> System.out.println("Processed value1: " + val));

// ifPresentOrElseを使用して、存在する場合は処理し、存在しない場合は別の処理を実行
opt1.ifPresentOrElse(
val -> System.out.println("Processed value1: " + val),
() -> System.out.println("Value is empty")
);

// チェーン処理:変換とフィルタ
Optional<Integer> lengthOpt = opt1
.map(String::toUpperCase) // 値を大文字に変換
.filter(val -> val.length() > 10) // 長さが10より大きいかフィルター
.map(String::length); // 文字列の長さを取得

lengthOpt.ifPresent(len -> System.out.println("Length of processed value: " + len));

// orを使用して、Optionalが空の場合に代替値を提供
Optional<String> opt2 = opt1.or(() -> Optional.of("Default Greeting"));
System.out.println("Result from opt2: " + opt2.get());

Optionalのベストプラクティス

  1. 戻り値としての利用:

    • Optionalはメソッドの戻り値に利用し、値が存在しない可能性を明示する。
  2. メソッド引数としては使用しない:

    • Optionalを引数にすると、呼び出し側に無駄なラッピングを強いるため、シンプルなnullチェックやオーバーロード、あるいは別のパラメータ設計を検討する。
  3. 直接get()の使用を避ける:

    • 値の取得はorElse, orElseThrow, ifPresentなどのメソッドを利用して、安全に扱う。
  4. 不必要なオブジェクト生成を避ける:

    • チェーン操作を利用して、コードをシンプルに保つ。

機械学習の理論から学ぶ効果的な勉強法

· 約4分
Mikyan
白い柴犬

僕は、機械学習のニューラルネットワークの理論が、人間にとっての最高の勉強法だと考えています。

勉強を進める上で、入力(Input)、処理(Process)、出力(Output)のサイクルは非常に重要です。ここでは、そのフレームワークをどのように学習に応用できるかを解説します。

勉強の効果を定義するにあたって、単に知識を暗記することではなく、**「何かの目標を達成するためのモデルを構築すること」**だと考えます。

以下は、僕が思っている効果的な勉強のフレームワークです:

勉強の第一歩は、何よりも「何を達成したいか」を明確にすることです。

  • 具体的な目標:試験合格、プロジェクト完遂、スキル習得など、測定可能な目標を設定する
  • 期限の設定:目標に向けたスケジュールを決め、進捗を確認できるようにする

2. Input:良質な情報を取り入れる

機械学習における有名な言葉に「GIGO(garbage in, garbage out)」があります。

  • 質の高い教材・情報:信頼できる書籍、論文、オンラインコースなどから学ぶ
  • 多様な視点:異なる角度や分野から情報を取り入れ、知識の幅を広げる

3. Process:自分に合った学習モデルで処理する

入力した情報を自分の中で消化し、知識として定着させるための「プロセス」が必要です。

学習モデルの構築:例えば、マインドマップ、ノート整理、問題演習など、目的に応じた学習法を試行錯誤する フィードバックの活用:定期的に理解度をテストし、間違いや疑問点を洗い出すことで、学習モデルを改善する

4. Output:成果を生み出し、評価する

学んだ内容を実際に「出力」することで、理解度を深め、記憶を定着させます。

アウトプットの方法:問題を解く、プレゼンテーションをする、ブログにまとめるなど 自己評価とフィードバック:成果を評価し、どこが不足しているのかを振り返る。これにより、次の学習プロセスの改善点が明確になります

5. 継続的な改善のサイクル

学習は一度きりの行動ではなく、継続的なサイクルです。

入力:最新の情報や知識を取り入れる 処理:取り入れた知識を自分のものにするための工夫をする 出力:成果を実際に試すことで、理解度を確認する フィードバック:出力をもとに学習方法を修正し、次のサイクルへ反映させる このサイクルを回し続けることで、着実に知識が深まり、より高度な問題にも対応できる力が養われます。

Git Cheat Sheet

· 約3分
Mikyan
白い柴犬

問題の背景

チームで開発する際、よくあるパターンとして、

  • developブランチから、featureブランチAを切って実装、PullRequestを作成し、レビュー待ち
  • その間に、featureブランチAにfeatureブランチBを切って、新しい機能を実装します
  • featureブランチAのレビューを経て承認され、スカッシュマージによってdevelopブランチへ統合され
  • この時、featureブランチBのコミットがfeatureブランチAの元のコミットに含まれてしまうことがあって、developブランチと多くのマージコンフリクトが発生してしまいました。

問題発生の原因

  • スカッシュマージによって個々のコミットがまとめられたため、ブランチBには依然としてブランチAの個々の変更が残っていること
  • その結果、developブランチに既に存在するスカッシュコミットとブランチBの変更が重複して適用され、コンフリクトが発生したこと

解決策:リベースによる解決

ブランチBをブランチA由来のコミットを取り除いて、developブランチ上のスカッシュコミットに合わせてリベースすることで、効率よく解決できます。

具体的には、以下の手順で行います:

git checkout feature/branch-b
git rebase --onto develop HASH_BASE feature/branch-b

このコマンドは、ブランチ B の中で HASH_BASE より後のコミットだけを、develop ブランチ上に再適用します。 HASH_BASEは、ブランチ A が develop から分岐した時点のコミットハッシュにすると、元ブランチ A のコミットがブランチ B に含まれなくなります。

リベース中にコンフリクトが発生した場合、各コンフリクト箇所で適切に修正し、 以下のコマンドでリベースを続行します。

git add <修正済みファイル>
git rebase --continue

リベースが正常に完了したら、ブランチ B の変更を develop ブランチへPRを作成してマージしましょう。

この方法により、履歴がクリーンになり、将来的なマージやデバッグ作業が大幅に楽になります。