Banyak kali saya menemukan diri saya nol memeriksa ketika mengambil nilai dari beberapa hierarki data untuk menghindari NullPointerExceptions, yang saya temukan cenderung rentan terhadap kesalahan dan membutuhkan banyak boilerplate.
Saya telah menulis rutin yang sangat sederhana yang memungkinkan saya melewatkan pemeriksaan nol saat mengambil objek ...
public final class NoNPE {
public static <T> T get(NoNPEInterface<T> in) {
try {
return in.get();
} catch (NullPointerException e) {
return null;
}
}
public interface NoNPEInterface<T> {
T get();
}
}
Saya menggunakannya agak seperti ini ...
Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());
Di atas mengakibatkan saya mendapatkan objek Kamar atau nol, tanpa harus memeriksa semua level orangtua nol.
Apa pendapat Anda tentang hal di atas? Apakah saya membuat pola yang bermasalah? Apakah ada cara yang lebih baik untuk melakukan ini menurut Anda?
java.util.Optional
alih-alih nol untuk mewakili data yang hilang? Ini menyediakan utilitas praktis baik untuk kasus yang Anda gambarkan maupun kasus di mana Anda ingin melanjutkan dengan data default alih-alih hanya mengembalikan kondisi kegagalan pada akhir rantai ..Option
(atauMaybe
) monad :)Jawaban:
Solusi Anda sangat cerdas. Masalah yang saya lihat adalah kenyataan bahwa Anda tidak tahu mengapa Anda mendapatkannya
null
? Apakah itu karena rumah itu tidak memiliki kamar? Apakah karena kota itu tidak memiliki rumah? Apakah itu karena negara itu tidak memiliki kota? Apakah karena adanull
di posisi 0 koleksi karena kesalahan bahkan ketika ada rumah di posisi 1 dan lebih besar?Jika Anda menggunakan
NonPE
kelas secara ekstensif , Anda akan memiliki masalah debugging yang serius. Saya pikir lebih baik untuk mengetahui di mana tepatnya rantai itu rusak daripada diam-diam mendapatkannull
yang bisa menyembunyikan kesalahan yang lebih dalam.Ini juga melanggar Hukum Demeter :
country.getTown().getHouses().get(0).getLivingRoom()
. Lebih sering daripada tidak, melanggar beberapa prinsip yang baik membuat Anda harus menerapkan solusi yang tidak lazim untuk memecahkan masalah yang disebabkan oleh melanggar prinsip tersebut.Rekomendasi saya adalah bahwa Anda menggunakannya dengan hati-hati dan mencoba menyelesaikan cacat desain yang membuat Anda harus mengeluarkan antipattern kereta (jadi Anda tidak harus menggunakan di
NonPE
mana-mana). Kalau tidak, Anda mungkin memiliki bug yang akan sulit dideteksi.sumber
Option
monad, Anda tidak peduli di mana dalam rantai nilai tidak ada. Saat Anda peduli, Anda mungkin akan menggunakan tipe yang berbeda, sepertiEither
.?.
dan?[]
operator. Salah satu contoh ketika Anda mungkin ingin menggunakan hal seperti itu adalah pengaturan sisi server hirarkis.var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;
Siapa yang peduli mengapa ada bagian yang batal? Mungkin Anda tidak dapat mengambil pengaturan. Mungkin Anda memutuskan untuk menghapus bagian dari pengaturan. Bagaimanapun, Anda tidak pernah bisa benar-benar mengandalkan untuk mendapatkan pengaturan server, jadi default biasanya adalah ide yang baik, dan Anda tidak akan peduli mengapa Anda tidak bisa mendapatkan pengaturan yang sebenarnya kecuali itu terjadi sangat sering ketika seharusnya tidak .settings.a.b.c
. Kemudian lagi, ini adalah contoh tunggal yang terisolasi.Idenya baik-baik saja, sebenarnya sangat bagus. Karena Java 8
Optional
jenis ada, penjelasan rinci dapat ditemukan di tipe Java Opsional . Contoh dengan apa yang Anda poskanDan selanjutnya.
sumber
Optional
adalah solusi yang lebih mudah dibaca dari keduanya, jika hanya karena - tidak seperti proposal Anda - itu adalah ungkapan yang sangat umum . Itu bahkan lebih ringkas daripada milikmu!Metode Anda bekerja cukup baik untuk tujuan yang dimaksudkan, meskipun kembali
null
ketika Anda mendapatkanNullPointerException
suara seperti desain yang buruk.Coba hindari
null
ketika Anda bisa dan hanya melewati mereka ketika mereka mewakili sesuatu atau memiliki makna khusus dan hanya mengembalikan mereka ketika mereka mewakili / berarti sesuatu - jika tidak, Anda harus melemparNullPointerException
. Ini menghindari bug dan kebingungan. Jika tidakObject
seharusnyanull
,NullPointer
harus dilempar. Jika suatu objek bisa jadinull
tidak ada yang salah ketika dilewatkan. Jika tidak, metode Anda di atas berfungsi.sumber
Saya bisa merasakan sakit Anda, tetapi solusi yang diusulkan adalah ide yang buruk.
NoNPE.get
.Optional.map
adalah apa yang Anda cari.Sebagai catatan,
NoNPEInterface
adalah duplikat darijava.util.function.Supplier
.Dalam beberapa kasus, Anda dapat mempertimbangkan menggunakan utils evaluasi ekspresi yang ada di banyak kerangka kerja (misalnya: EL, SpEL):
sumber