null の全体像
null は「参照がどのオブジェクトも指していない」ことを表す特別な値です。プリミティブ型(int、double、boolean など)には存在せず、参照型(String、配列、クラス、インターフェースなど)でのみ使われます。変数・フィールド・配列要素が未初期化のままでも、参照型なら既定値は null になります。便利な反面、操作を誤ると NullPointerException(NPE)の温床になります。
基本動作とデフォルト値
デフォルト初期化の挙動
class Box {
String name; // 既定で null
int count; // 既定で 0
}
Java参照型のフィールドや配列要素は、初期化を省略すると null です。ローカル変数はデフォルト初期化されないため、使う前に必ず代入してください(未初期化のまま使うとコンパイルエラー)。
null 参照に対する操作
String s = null;
// s.length(); // NPE(メソッド呼び出しは不可)
boolean eq = (s == null); // 比較は可能
Java「参照が無い」ため、メソッド呼び出し・フィールドアクセスはできません。使用前に null ガードを入れるのが基本です。
NullPointerException の典型と対策(重要ポイントの深掘り)
典型パターン
String s = null;
if (s.equals("OK")) { /* ... */ } // NPE(左が null)
Java対策は「null の可能性がある側を右に置く」またはユーティリティを使います。
if ("OK".equals(s)) { /* ... */ } // 安全(左は非 null)
if (java.util.Objects.equals(s, "OK")) { /* ... */ } // どちらかが null でも安全
Javaコレクション操作でも起きがちです。
java.util.Map<String, String> m = new java.util.HashMap<>();
String v = m.get("key"); // 見つからなければ null(キーが無いのか、値が null なのか判別不能)
Java必要なら「キー有無の判定」と「値の null」を分けて扱います。
if (m.containsKey("key")) {
String v2 = m.get("key"); // 値が null かどうかは別途判断
}
Java早期検証と防御的プログラミング
API 入口で null を許可しないなら、早期に検証して失敗を明示化します。
import java.util.Objects;
static String upper(String s) {
Objects.requireNonNull(s, "s must not be null");
return s.toUpperCase();
}
Java許可するなら「既定値で置き換える」設計にします。
static String safeUpper(String s) {
if (s == null) return "";
return s.toUpperCase();
}
Javanull の比較と安全な等価判定
== と equals の使い分け
== は「参照が同じか」を比較します。値の比較は equals を使いますが、左オペランドが null だと NPE になります。次のどれかに統一すると安全です。
"OK".equals(s) // 左を非 null に固定
java.util.Objects.equals(s, "OK") // どちらも null セーフ
Java並び替えや検索での null セーフ
並び替えは Comparator の nullsFirst/nullsLast を使うと安全です。
var list = new java.util.ArrayList<>(java.util.List.of("b", null, "a"));
list.sort(java.util.Comparator.nullsLast(String::compareTo));
JavaAPI 設計の方針:許容・禁止・Optional(重要ポイントの深掘り)
「null を許すか」を最初に決める
- 禁止するなら、入口で
requireNonNull。ドキュメントにも「null不可」を明記。 - 許可するなら、戻り値や副作用の契約を具体的に書く(例:nullの場合は空文字を返す)。
Optional で「有無」を型表現する
null を返す代わりに Optional<T> で結果の有無を示すと、呼び出し側が安全に扱えます(ライブラリ的コードに特に有効)。
import java.util.Optional;
static Optional<String> findStartsWith(String[] arr, String prefix) {
for (String s : arr) if (s.startsWith(prefix)) return Optional.of(s);
return Optional.empty();
}
// 呼び出し側
String v = findStartsWith(new String[]{"java","js"}, "ja").orElse("なし");
Java注意点として、Optional は「戻り値」に向いています。フィールドや引数に使うのは過剰になりがちです(呼び出しが煩雑になるため)。
コレクション・ストリームと null の扱い
コレクションに null を入れる是非
リストやマップの値として null を許すと、バグの温床になります。可能なら「空のコレクション」「Optional」「既定値」で置き換え、null を避ける方針にします。
java.util.List<String> xs = java.util.List.of(); // 「なし」を空リストで表現
Javaストリームでの null フィルタ
ストリームは null を要素に含めないのが基本です。混入し得るなら最初にフィルタで除去します。
var list = java.util.Arrays.asList("a", null, "b");
var cleaned = list.stream().filter(java.util.Objects::nonNull).toList();
Java実例で身につける
例 1: null セーフな比較と分岐
public class NullCompare {
static boolean isOk(String s) {
return java.util.Objects.equals(s, "OK");
}
public static void main(String[] args) {
System.out.println(isOk(null)); // false
System.out.println(isOk("OK")); // true
}
}
Java例 2: Optional を使った探索
import java.util.*;
public class FindOpt {
static Optional<String> find(String[] arr, String key) {
for (String s : arr) if (java.util.Objects.equals(s, key)) return Optional.of(s);
return Optional.empty();
}
public static void main(String[] args) {
var r = find(new String[]{"A","B"}, "C").orElse("not found");
System.out.println(r);
}
}
Java例 3: 入口で方針を固定(禁止と既定値)
public class Policy {
static String mustNonNull(String s) {
java.util.Objects.requireNonNull(s, "s must not be null");
return s.trim();
}
static String allowNull(String s) {
return (s == null) ? "" : s.trim();
}
}
Java仕上げのアドバイス(重要部分のまとめ)
null は「参照がない」ことを示すだけで、操作はできません。NPEの典型を避けるために、equals の呼び出し順や Objects.equals を習慣化し、API入口で「許容か禁止か」を決めて一貫させる。返り値の「有無」は Optional が有効で、コレクションには極力 null を入れない。マップの null は「キー無し」との区別が曖昧になるため、方針を決めて運用する。迷ったら「早期ガード」「既定値」「Optional」の3手で、nullを“仕様として”扱い、偶発的なエラー源にしないことが大切です。
