「安全get」は「落ちてもおかしくない場所に、クッションを敷いておく」技
List#get や Map#get は、とてもよく使う基本メソッドですが、
そのまま使うと、意外と簡単に例外や NullPointerException を起こします。
インデックスが範囲外だった。
キーが存在しなかった。
そもそもコレクション自体が null だった。
「落ちてもおかしくない場所」に、そのまま生身で突っ込むのではなく、
あらかじめ“クッション(安全get)”を敷いておく——それが今回のテーマです。
List 向けの安全get
範囲外アクセスで落ちないようにする
まずは List から安全に要素を取り出すユーティリティです。
import java.util.List;
public final class SafeGets {
private SafeGets() {}
public static <T> T getOrNull(List<T> list, int index) {
if (list == null) {
return null;
}
if (index < 0 || index >= list.size()) {
return null;
}
return list.get(index);
}
}
Java使い方はこうです。
List<String> names = List.of("山田", "佐藤", "鈴木");
String a = SafeGets.getOrNull(names, 1); // "佐藤"
String b = SafeGets.getOrNull(names, 10); // null(安全に失敗)
String c = SafeGets.getOrNull(null, 0); // null
Javaここでの重要ポイントは二つです。
一つ目は、「インデックスが範囲外でも IndexOutOfBoundsException を投げずに null を返す」ことです。
「とりあえずあれば使う、なければ諦める」という場面では、例外より null の方が扱いやすいことが多いです。
二つ目は、「リスト自体が null のときも null を返す」ことです。
呼び出し側は「とりあえず安全に要素を取りたい」とだけ考えればよく、list != null のチェックを毎回書かなくて済みます。
デフォルト値を返すバージョン
「null ではなく、決めたデフォルト値を返したい」こともあります。
public static <T> T getOrDefault(List<T> list, int index, T defaultValue) {
if (list == null) {
return defaultValue;
}
if (index < 0 || index >= list.size()) {
return defaultValue;
}
T value = list.get(index);
return value != null ? value : defaultValue;
}
Java使い方の例です。
List<Integer> numbers = List.of(10, 20);
int x = SafeGets.getOrDefault(numbers, 1, 0); // 20
int y = SafeGets.getOrDefault(numbers, 5, 0); // 0(デフォルト)
int z = SafeGets.getOrDefault(null, 0, 0); // 0
Javaここでの重要ポイントは、
「“範囲外”と“リストが null”と“値が null”のすべてを、同じようにデフォルト扱いにしている」ことです。
業務として「null は未設定扱いでよい」「なければ 0(空文字など)でよい」と決められるなら、
このパターンはとても書きやすくなります。
Map 向けの安全get
Map が null でも、キーがなくても落ちない
次は Map 版です。
import java.util.Map;
public final class SafeGets {
private SafeGets() {}
public static <K, V> V getOrNull(Map<K, V> map, K key) {
if (map == null) {
return null;
}
return map.get(key); // キーがなければ null
}
}
Java使い方はこうです。
Map<String, Integer> scores = Map.of(
"山田", 80,
"佐藤", 90
);
Integer a = SafeGets.getOrNull(scores, "山田"); // 80
Integer b = SafeGets.getOrNull(scores, "鈴木"); // null
Integer c = SafeGets.getOrNull(null, "山田"); // null
Javaここでの重要ポイントは、
「Map 自体が null でも安全に null を返す」ことです。
map.get(key) をそのまま呼ぶと、map が null のときに NullPointerException になりますが、SafeGets.getOrNull を挟めば、「なければ null」という一貫した挙動になります。
デフォルト値を返すバージョン
Map#getOrDefault もありますが、「Map が null のとき」までは面倒を見てくれません。
そこで、もう一枚ラッパーをかぶせます。
public static <K, V> V getOrDefault(
Map<K, V> map,
K key,
V defaultValue
) {
if (map == null) {
return defaultValue;
}
V value = map.get(key);
return value != null ? value : defaultValue;
}
Java使い方はこうです。
Integer score = SafeGets.getOrDefault(scores, "鈴木", 0); // 0
Integer score2 = SafeGets.getOrDefault(null, "山田", 0); // 0
Javaここでの重要ポイントは、
「“Map が null”“キーがない”“値が null”のすべてを、同じようにデフォルト扱いにしている」ことです。
プロジェクトとして「null は使わず、必ずデフォルト値に正規化する」と決めるなら、
このパターンを徹底するだけで、NPE のリスクがかなり減ります。
Optional を使った安全get
「null を返したくない」場合の書き方
「null を返すのも怖いから、Optional で包みたい」という考え方もあります。
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class SafeGets {
private SafeGets() {}
public static <T> Optional<T> getOptional(List<T> list, int index) {
if (list == null) {
return Optional.empty();
}
if (index < 0 || index >= list.size()) {
return Optional.empty();
}
return Optional.ofNullable(list.get(index));
}
public static <K, V> Optional<V> getOptional(Map<K, V> map, K key) {
if (map == null) {
return Optional.empty();
}
return Optional.ofNullable(map.get(key));
}
}
Java使い方はこうです。
Optional<String> maybeName = SafeGets.getOptional(names, 1);
maybeName.ifPresent(System.out::println);
String nameOrDefault =
SafeGets.getOptional(names, 10)
.orElse("不明");
Javaここでの重要ポイントは二つです。
一つ目は、「“あるかもしれない/ないかもしれない”を型で表現している」ことです。Optional<T> を返すことで、「呼び出し側は必ず orElse や ifPresent などで扱う必要がある」状態になります。
二つ目は、「null を直接返さないことで、“うっかり NPE”を減らせる」ことです。
ただし、Optional を多用しすぎるとコードが重くなるので、「外部公開 API など境界部分で使う」のがバランスのよい使い方です。
安全getをどこまで徹底するか
「例外で気づきたい場所」と「静かに諦めたい場所」を分ける
安全getは便利ですが、何でもかんでも例外を潰して null やデフォルトにしてしまうと、
本来気づくべきバグに気づけなくなることもあります。
たとえば、「絶対に存在するはずのインデックス」「必ず入っているはずのキー」がなかった場合、
本当は例外で落ちてほしい場面もあります。
だからこそ、次のように使い分けるのが大事です。
「ユーザー入力や外部データなど、“なくてもおかしくない”もの」
→ 安全getで null/デフォルトにして、処理を続行する。
「アプリ内部のロジック上、“必ずあるはず”のもの」
→ 通常の get を使い、なければ例外で気づくようにする。
安全getは、「落ちてもおかしくない場所」にだけクッションを敷くイメージで使うと、
コードの意図がとてもクリアになります。
まとめ:安全getユーティリティで身につけてほしい感覚
安全getは、
単に「例外を避けるテクニック」ではなく、
「“ないかもしれない”を前提に、どう振る舞うかを設計する技術」です。
List では「範囲外」「null リスト」を安全に扱う。
Map では「null Map」「キーなし」「値 null」をどう扱うかを決める。
必要に応じてデフォルト値版・Optional 版を用意し、「呼び出し側の責務」をはっきりさせる。
「ここは安全get」「ここはあえて例外でいい」という線引きを、業務の意味とセットで考える。
あなたのコードのどこかに、if (list != null && index >= 0 && index < list.size()) { ... }if (map != null && map.get(key) != null) { ... }
のような条件が何度も出てきているなら、
それを一度「安全getユーティリティ」にまとめられないか眺めてみてください。
その小さな整理が、
「落ちてもおかしくない場所に、ちゃんとクッションを敷けるエンジニア」への、
確かな一歩になります。

