Bagaimana kita harus mengelola aliran jdk8 untuk nilai null

88

Halo sesama pengembang Java,

Saya tahu subjeknya mungkin sedikit in advancekarena JDK8 belum dirilis (dan bukan untuk saat ini ..) tetapi saya membaca beberapa artikel tentang ekspresi Lambda dan khususnya bagian yang terkait dengan API koleksi baru yang dikenal sebagai Stream.

Berikut adalah contoh yang diberikan dalam artikel Majalah Java (ini adalah algoritma populasi berang-berang ..):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

Pertanyaan saya adalah apa yang terjadi jika di tengah iterasi internal Set, salah satu otter adalah null?

Saya berharap NullPointerException dilemparkan tetapi mungkin saya masih terjebak dalam paradigma pengembangan sebelumnya (non-fungsional), dapatkah seseorang mencerahkan saya tentang bagaimana hal ini harus ditangani?

Jika ini benar-benar memunculkan NullPointerException, menurut saya fiturnya cukup berbahaya dan harus digunakan hanya seperti di bawah ini:

  • Pengembang untuk memastikan tidak ada nilai null (mungkin menggunakan .filter sebelumnya (o -> o! = Null))
  • Pengembang untuk memastikan aplikasi tidak pernah menghasilkan null otter atau objek NullOtter khusus untuk ditangani.

Apa opsi terbaik, atau opsi lain?

Terima kasih!

sejuk
sumber
3
Menurut saya, terserah programmer untuk melakukan hal yang benar di sini; JVM dan compiler hanya dapat melakukan banyak hal. Perhatikan bahwa beberapa implementasi kumpulan tidak akan mengizinkan nilai null.
fge
19
Anda dapat menggunakan filter(Objects::nonNull)dengan Objectsdarijava.utils
Benj

Jawaban:

44

Pemikiran saat ini tampaknya untuk "mentolerir" nulls, yaitu untuk mengizinkan mereka secara umum, meskipun beberapa operasi kurang toleran dan mungkin berakhir dengan membuang NPE. Lihat pembahasan nulls di milis grup pakar Perpustakaan Lambda, khususnya pesan ini . Konsensus seputar opsi # 3 kemudian muncul (dengan keberatan penting dari Doug Lea). Jadi ya, kekhawatiran OP tentang pipa yang meledak dengan NPE adalah valid.

Bukan tanpa alasan Tony Hoare menyebut nulls sebagai "Kesalahan Miliaran Dolar." Berurusan dengan nol adalah rasa sakit yang nyata. Bahkan dengan koleksi klasik (tanpa mempertimbangkan lambda atau aliran) null bermasalah. Seperti yang disebutkan fge dalam komentar, beberapa koleksi mengizinkan nulls dan yang lainnya tidak. Dengan koleksi yang memungkinkan null, ini memperkenalkan ambiguitas ke dalam API. Misalnya, dengan Map.get () , pengembalian null menunjukkan bahwa kunci ada dan nilainya null, atau bahwa kunci tidak ada. Kita harus melakukan pekerjaan ekstra untuk membedakan kasus-kasus ini.

Biasanya null digunakan untuk menunjukkan tidak adanya nilai. Pendekatan untuk menangani ini yang diusulkan untuk Java SE 8 adalah dengan memperkenalkan java.util.Optionaltipe baru , yang merangkum ada / tidaknya nilai, bersama dengan perilaku penyediaan nilai default, atau melempar pengecualian, atau memanggil fungsi, dll. Jika nilainya tidak ada. Optionalhanya digunakan oleh API baru, meskipun, semua yang lain di sistem masih harus menghadapi kemungkinan null.

Saran saya adalah untuk menghindari referensi nol yang sebenarnya sejauh mungkin. Sulit untuk melihat dari contoh yang diberikan bagaimana mungkin ada Otter "null". Tetapi jika diperlukan, saran OP untuk menyaring nilai null, atau memetakannya ke objek sentinel ( Pola Objek Null ) adalah pendekatan yang bagus.

Stuart Marks
sumber
3
Mengapa menghindari nulls? Mereka digunakan dalam database secara ekstensif.
Raffi Khatchadourian
6
@RaffiKhatchadourian Ya, null digunakan dalam database, tetapi sama-sama bermasalah. Lihat ini dan ini dan baca semua jawaban dan komentar. Juga pertimbangkan dampak SQL null pada ekspresi boolean: en.wikipedia.org/wiki/… ... ini adalah sumber kesalahan kueri yang kaya.
Stuart Marks
1
@RaffiKhatchadourian Karena nullmenyebalkan, itu sebabnya. Acc. menurut survei yang saya lihat (tidak dapat menemukannya), NPE adalah pengecualian nomor 1 di Jawa. SQL bukanlah bahasa pemrograman tingkat tinggi, tentu saja tidak berfungsi, yang membenci null, jadi tidak peduli.
Abhijit Sarkar
95

Meskipun jawabannya 100% benar, sedikit saran untuk meningkatkan nullpenanganan kasus dari daftar itu sendiri dengan Opsional :

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

Bagian ini Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)akan memungkinkan Anda untuk menangani dengan baik kasus ketika listOfStuffnull dan mengembalikan emptyList daripada gagal dengan NullPointerException.

Johnny
sumber
2
Saya suka ini, menghindari secara eksplisit memeriksa null.
Chris
1
ini terlihat paling jelas .. bagus dan persis apa yang saya butuhkan
Abdullah Al Noman
Ini secara eksplisit memeriksa null, hanya dengan cara yang berbeda. Jika diizinkan menjadi nol, itu harus menjadi Opsional, itu idenya, bukan? Larang semua nilai null dengan opsional. Selain itu, merupakan praktik yang buruk untuk mengembalikan null alih-alih daftar kosong, jadi ini menunjukkan dua kode bau jika saya melihat ini: tidak ada Opsional yang digunakan, dan tidak ada aliran kosong yang dikembalikan. Dan yang terakhir tersedia selama lebih dari 20 tahun, jadi itu matang ...
Koos Gadellaa
bagaimana jika saya ingin mengembalikan null bukan daftar kosong?
Ashburn RK
@AburnRK itu praktik yang buruk. Anda harus mengembalikan daftar kosong.
Johnny
70

Jawaban Stuart memberikan penjelasan yang bagus, tapi saya ingin memberikan contoh lain.

Saya mengalami masalah ini ketika mencoba untuk melakukan reducedi Stream yang berisi nilai null (sebenarnya itu LongStream.average(), yang merupakan jenis pengurangan). Karena rata-rata () kembali OptionalDouble, saya berasumsi Stream dapat berisi null tetapi NullPointerException dilemparkan. Ini karena penjelasan Stuart tentang null v. Empty.

Jadi, seperti yang disarankan OP, saya menambahkan filter seperti ini:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

Atau seperti yang ditunjukkan di bawah ini, gunakan predikat yang disediakan oleh Java API:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

Dari diskusi milis Stuart ditautkan: Brian Goetz tentang nulls di Streams

bparry
sumber
19

Jika Anda hanya ingin memfilter nilai null dari aliran, Anda dapat menggunakan metode referensi ke java.util.Objects.nonNull (Object) . Dari dokumentasinya:

Metode ini ada untuk digunakan sebagai Predikat ,filter(Objects::nonNull)

Sebagai contoh:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

Ini akan mencetak:

Foo
Bar
Andy Thomas
sumber
7

Contoh bagaimana menghindari null misalnya menggunakan filter sebelum groupingBy

Filter instance null sebelum groupingBy.

Berikut ini contohnya

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
Ilan M
sumber