なぜこれがJava.lang.NullPointerException
をスローするのですか?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
strings
の最初の項目はnull
で、これは完全に受け入れられる値です。さらに、findFirst()
は オプション を返します。これは、findFirst()
がnull
sを処理できるようにするためにさらに意味があります。
編集:orElse()
をより曖昧に更新しました。
この理由は、戻り値でのOptional<T>
の使用です。 Optionalにはnull
を含めることはできません。基本的に、「存在しない」と「存在するが、null
」に設定されている状況を区別する方法はありません。
ドキュメント がfindFirst()
でnull
が選択されている場合、状況を明示的に禁止しているのはそのためです。
スロー:
NullPointerException
-選択した要素がnull
の場合
既に説明した のように、API設計者は、開発者がnull
値を扱い、値を同じように扱いたくないとは思いません。
それでもやりたい場合は、シーケンスを適用して明示的に行うことができます
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
ストリームに。最初の要素がない場合、または最初の要素がnull
である場合、結果は両方のケースで空のオプションになります。だからあなたの場合、あなたは
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
最初の要素が存在しないかnull
の場合、null
値を取得します。
これらのケースを区別したい場合、単にflatMap
ステップを省略できます:
Optional<String> firstString = strings.stream()
.map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
firstString.orElse("first element is null"));
これは、更新された質問と大差ありません。 "no such element"
を"StringWhenListIsEmpty"
に、"first element is null"
をnull
に置き換えるだけです。ただし、条件式が気に入らない場合は、次のように達成することもできます。
String firstString = strings.stream().skip(0)
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
現在、firstString
は、要素は存在するがnull
である場合はnull
になり、要素が存在しない場合は"StringWhenListIsEmpty"
になります。
次のコードは、findFirst()
をlimit(1)
に置き換え、orElse()
をreduce()
に置き換えます。
String firstString = strings.
stream().
limit(1).
reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
は、reduce
に到達する要素を1つだけ許可します。 BinaryOperator
に渡されるreduce
は、その1要素を返します。reduce
に到達する要素がない場合は"StringWhenListIsEmpty"
を返します。
このソリューションの利点は、Optional
が割り当てられておらず、BinaryOperator
ラムダが何も割り当てていないことです。
Java.util.Objects.nonNull
を使用して、検索する前にリストをフィルタリングできます
何かのようなもの
list.stream().filter(Objects::nonNull).findFirst();
オプションは「値」タイプであると想定されています。 (詳細は javadoc :をお読みください)JVMは、すべてのOptional<Foo>
をFoo
だけに置き換えて、すべてのボックス化およびボックス化解除コストを削除することもできます。 null
Fooは、空のOptional<Foo>
を意味します。
ブールフラグを追加せずに、null値でOptionalを許可する設計が可能です-センチネルオブジェクトを追加するだけです。 (this
をセンチネルとして使用することもできます。Throwable.causeを参照してください)
Optionalがnullをラップできないという決定は、ランタイムコストに基づいていません。これは非常に論争の的だった問題であり、メーリングリストを調査する必要があります。この決定はすべての人を納得させるものではありません。
いずれにせよ、Optionalはnull値をラップできないため、findFirst
のような場合には隅に追いやられます。 null値は非常にまれである(Streamはnull値を禁止することさえ考えられていた)と考えたため、空のストリームではなくnull値に例外をスローする方が便利です。
回避策は、ボックスnull
です。
class Box<T>
static Box<T> of(T value){ .. }
Optional<Box<String>> first = stream.map(Box::of).findFirst();
(彼らはすべてのOOP問題の解決策は別のタイプを導入することだと言う:)