Java9で追加された List#of
などのファクトリーメソッドや、Java10で追加された Collectors#toUnmodifiableList
などのイミュータブルなコレクション生成をよく使っているが、nullを含んだ値を指定したら NullPointerException
が発生した。
標準APIやGuavaのImmutableListなどでも、不変のコレクションを生成するメソッドでは要素にnullを設定できなかったのでメモ。
Java標準API
List
Java9で追加された List#of
、Java10で追加された List#copyOf
や Collectors#toUnmodifiableList
で、変更不能リストを生成する場合、要素はnull不可となる。
それぞれ List および Collectors のJavadocに記載あり。
List#of
および List#copyOf
については、Listのクラスに対するJavadocにも詳細がある。
Set
Listと同様、 Set#of
、 Set#copyOf
、 Collectors#toUnmodifiableSet
で変更不能セットを生成する場合、要素はnull不可となる。
これまた Set のJavadocに記載あり、クラスに対するJavadocに詳細があるのも同様。
Map
こちらも Map#of
、 Map#copyOf
、 Collectors#toUnmodifiableMap
で変更不能マップを生成する場合、キーおよび値はnull不可となる。 Map のJavadocに記載があるのも同じ。
Guava
ImmutableList、
ImmutableSet、 ImmutableMap など、 com.google.common.collect
内の Immutable
で始まるクラスは、すべて要素やキーと値がnull不可となっている。
詳細は、ImmutableCollection に記載あり。 ImmutableMap
は継承関係にないが、継承しているか否かにかかわらず、 Immutable
で始まるクラスに適応されるとのこと。
The remainder of this documentation applies to every public Immutable- type in this package, whether it is a subtype of ImmutableCollection or not.
nullを含むコレクションを変更不可コレクションにするには
nullを要素に含んだListやSet、値に含んだMapを変更不可にするには、Optionalでラップするのが手っ取り早い。
ListやSetであれば、Streamにしてmapで変換すればいい。
// of の置き換え Stream.of(...) .map(Optional::ofNullable) .collect(Collectors.toUnmodifiableList()); // copyOf の置き換え values.stream() .map(Optional::ofNullable) .collect(Collectors.toUnmodifiableList());
Mapの場合、すでにコピー元のインスタンスが存在すれば、Guavaの Maps#transformValues を使うと簡単。
Map.copyOf(Maps.transformValues(map, Optional::ofNullable));
Map#of
を使っていた場合、変更可能なマップを生成する同様のメソッドがないため、HashMapなど適当なMapインスタンスを生成した後に、 Maps#transformValues
で変換か。
キーがnullのMapは、まず使わないので無視してよさそう。
振り返り
nullが入らないことで、nullチェックが不要となり、かなりシンプルに使えるようになるため、この仕様はありがたい。
ただ、ListやSet、Mapといったインタフェースで変数や戻り値を宣言すると、要素などがnull不可という情報が消えてしまう。Guavaが使える環境であれば、ImmutableCollectionのJavadocにあるように、ImmutableListなどを変数や戻り値に使っておくのがよさそう。
For field types and method return types, you should generally use the immutable type (such as ImmutableList) instead of the general collection interface type (such as List). This communicates to your callers all of the semantic guarantees listed above, which is almost always very useful information.