Past & Future of null in Java
JJUG CCC 2015 Spring #CCC_CD1
Javaの鉱脈 連載中
Siden
https://github.com/taichi/siden
今日の目標
nullについて考え直すきっかけを提供したい
NullPointerException(NPE)をみたくない
null が無ければNPEも無いはずだ
そうだ null、 撲滅しよう。
null とは?
cc-by-sa-2.0-fr
Null References: The Billion Dollar Mistake
NULL参照の起源
ALGOL 60の後継言語であるALGOL 68へのプロポーザルとして幾つかの言語が設計された。
ホーア先生とヴィルト先生はオブジェクト指向言語であるALGOL Wを設計した。
コンパイラによって参照が安全である事を保証する仕組みを設計する過程で安易にNULL参照を導入してしまった。
ALGOL WはALGOL 68として採択されなかったが、その後、ヴィルト先生の設計したPascalではポインタ型の一部としてnil ポインタが採用された。
Javaにおけるnullとは
Java以外のnull
Ruby
JavaScript
Go
Scala
SQL(RDB)
NOT | |
TRUE | F |
FALSE | T |
UNKNOWN | U |
AND | T | F | U |
TRUE | T | F | U |
FALSE | F | F | U |
UNKNOWN | U | U | U |
OR | T | F | U |
TRUE | T | T | T |
FALSE | T | F | U |
UNKNOWN | T | U | U |
Javaのnullを相対的に理解する
NPEは何故起きるのか?
言語仕様の落とし穴
ライブラリの落とし穴
設計における考慮不足
言語仕様の落とし穴
オートボクシング
NullPointerException
言語仕様の落とし穴
拡張forループ
NullPointerException
言語仕様の落とし穴
synchronized
NullPointerException
言語仕様の落とし穴
throw
NullPointerException
言語仕様の落とし穴
try-with-resources
OK
言語仕様の落とし穴
発生するケースを覚えてしまえばよい
正直、コンパイラにチェックして欲しい
ライブラリの落とし穴
java.util.Comparator
NullPointerException
ライブラリの落とし穴
java.util.NavigableSet
NullPointerException
ライブラリの落とし穴
SpringのComponentScan
ライブラリの落とし穴
CDIのbean-discovery-modeとかscanとか
ライブラリの落とし穴
各自頑張って下さいとしか言いようがない
設計における考慮不足
「何もないこと」について考え直そう
「何もない」を値で表すと扱い易くなるかもしれない
「メソッドの有効な戻り値がない」時、呼び出し側にできることは無いだろうか?
「有効な値を返さないかもしれない」メソッドを効率よく、合理的に調べる方法は無いだろうか?
型のあるnullは作れないだろうか?
無効な値でメソッドを呼び出されないようにしたい
「何もない値」を表現する方法
空のオブジェクトを使う
型のあるnullを表現する方法
NullObjectパターン
「有効な値を返さないかもしれない」メソッド
java.util.Optional<T>
java.util.Optional<T>
メソッドの戻り値が無効な時、呼び出し側にできること
あらかじめ代替となる値を渡す
java.util.Map#getOrDefault
java.util.Objects#toString
戻り値の状態に併せて処理を行う
伝統的なnullチェック
戻り値の状態に併せて処理を行う
Optionalの誤った使い方
何が誤っているのか
Optionalを使っているが処理構造に変化が無い
Optionalを使わない時よりもコードが増えている
問題を解決するどころか問題を増やしている
戻り値の状態に併せて処理を行う
Optional#ifPresentで値がある時だけ処理する
戻り値の状態に併せて処理を行う
Optional#orElseで代替の値を指定する
戻り値の状態に併せて処理を行う
OptionalInt#orElseで代替の値を指定する
プリミティブ型で「無効な値」を扱えるようになった
戻り値の状態に併せて処理を行う
Optional#filterで値の有効性を後から決める
戻り値の状態に併せて処理を行う
Optional#mapで有効な値を変換する
戻り値の状態に併せて処理を行う
Optional#mapはnullで値を無効化できる
戻り値の状態に併せて処理を行う
沢山の猫が入った箱があるとする
戻り値の状態に併せて処理を行う
Optional#mapを使うとOptionalが入れ子になる
戻り値の状態に併せて処理を行う
Optional#flatMapでOptionalの入れ子を防ぐ
戻り値の状態に併せて処理を行う
Optional#flatMapでnullを返してはいけない
無効な値でメソッドを呼び出されないために
JSR305やIDE組込みの@NonNull を使う
アノテーションは強制力が弱いが影響も少ない
コンパイラやIDEの力を借りる
Checker Frameworkなら更に高度な解析ができる
@NonNullで言語仕様の落とし穴を防ぐ
要素にnullが含まれないことを宣言する
nullを拒絶する
java.util.Objects#requireNonNull
com.google.common.base.Preconditions#checkNotNull
早期にインターフェースエラーを発見するのが目的
この方針ならユニットテストを潤沢に用意すること
assert は歴史の闇に消えた
Java7に入りそうで入らなかった機能
最後に
Javaには巨大な資産がある
ゆるやかによりよい形へ移行しよう
ご清聴ありがとうございました