Saat membaca kode sumber JDK, saya merasa umum bahwa penulis akan memeriksa parameter jika mereka nol dan kemudian melemparkan NullPointerException () baru secara manual. Mengapa mereka melakukannya? Saya pikir tidak perlu melakukannya karena itu akan membuang NullPointerException () baru ketika memanggil metode apa pun. (Berikut adalah beberapa kode sumber HashMap, misalnya :)
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
java
nullpointerexception
LiJiaming
sumber
sumber
ArgumentNullException
dalam kasus-kasus seperti itu (bukanNullReferenceException
) - itu sebenarnya pertanyaan yang sangat bagus tentang mengapa Anda mengajukanNullPointerException
secara eksplisit di sini (daripada yang berbeda).IllegalArgumentException
atauNullPointerException
untuk argumen nol. Konvensi JDK adalah yang terakhir.Jawaban:
Ada sejumlah alasan yang muncul di benak, beberapa terkait erat:
Gagal-cepat: Jika gagal, lebih baik cepat gagal daripada nanti. Ini memungkinkan masalah untuk ditangkap lebih dekat ke sumbernya, membuatnya lebih mudah untuk diidentifikasi dan pulih. Ini juga menghindari pemborosan siklus CPU pada kode yang pasti gagal.
Maksud: Melempar pengecualian secara eksplisit membuat jelas bagi pengelola bahwa kesalahan itu ada dengan sengaja dan penulis menyadari konsekuensinya.
Konsistensi: Jika kesalahan dibiarkan terjadi secara alami, itu mungkin tidak terjadi di setiap skenario. Jika tidak ada pemetaan yang ditemukan, misalnya,
remappingFunction
tidak akan pernah digunakan dan pengecualian tidak akan dibuang. Memvalidasi input terlebih dahulu memungkinkan perilaku yang lebih deterministik dan dokumentasi yang lebih jelas .Stabilitas: Kode berkembang seiring waktu. Kode yang mengalami pengecualian secara alami mungkin, setelah sedikit refactoring, berhenti melakukannya, atau melakukannya dalam keadaan yang berbeda. Melemparkannya secara eksplisit membuat perilaku cenderung berubah secara tidak sengaja.
sumber
new NullPointerException(message)
konstruktor untuk menjelaskan apa yang null. Bagus untuk orang yang tidak memiliki akses ke kode sumber Anda. Mereka bahkan membuat ini satu-liner di JDK 8 denganObjects.requireNonNull(object, message)
metode utilitas.Ini untuk kejelasan, konsistensi, dan untuk mencegah pekerjaan tambahan yang tidak perlu dilakukan.
Pertimbangkan apa yang akan terjadi jika tidak ada klausa penjaga di bagian atas metode ini. Itu akan selalu memanggil
hash(key)
dangetNode(hash, key)
bahkan ketikanull
telah dilewati untukremappingFunction
sebelum NPE dilemparkan.Lebih buruk lagi, jika
if
kondisinyafalse
maka kita mengambilelse
cabang, yang tidak menggunakanremappingFunction
sama sekali, yang berarti metode tidak selalu membuang NPE ketika anull
dilewatkan; apakah itu tergantung pada kondisi peta.Kedua skenario itu buruk. Jika
null
bukan nilai yang valid untukremappingFunction
metode ini harus secara konsisten melempar pengecualian terlepas dari keadaan internal objek pada saat panggilan, dan harus melakukannya tanpa melakukan pekerjaan yang tidak perlu yang tidak ada gunanya mengingat bahwa itu hanya akan melempar. Akhirnya, itu adalah prinsip yang baik dari kode yang bersih dan jelas untuk memiliki penjaga di depan sehingga siapa pun yang meninjau kode sumber dapat dengan mudah melihat bahwa itu akan melakukannya.Bahkan jika pengecualian saat ini dilemparkan oleh setiap cabang kode, ada kemungkinan bahwa revisi kode di masa depan akan mengubahnya. Melakukan pemeriksaan di awal memastikan pasti akan dilakukan.
sumber
Selain alasan yang tercantum oleh jawaban sempurna @ shmosel ...
Kinerja: Mungkin ada / telah ada manfaat kinerja (pada beberapa JVM) untuk melempar NPE secara eksplisit daripada membiarkan JVM melakukannya.
Itu tergantung pada strategi yang Java interpreter dan JIT compiler ambil untuk mendeteksi dereferencing dari null pointer. Salah satu strategi adalah untuk tidak menguji null, tetapi malah menjebak SIGSEGV yang terjadi ketika instruksi mencoba mengakses alamat 0. Ini adalah pendekatan tercepat dalam kasus di mana referensi selalu valid, tetapi mahal dalam kasus NPE.
Tes eksplisit untuk
null
dalam kode akan menghindari kinerja SIGSEGV hit dalam skenario di mana NPE sering terjadi.(Saya ragu bahwa ini akan menjadi optimasi mikro yang bermanfaat dalam JVM modern, tetapi bisa saja di masa lalu.)
Kompatibilitas: Kemungkinan alasan tidak ada pesan dalam pengecualian adalah untuk kompatibilitas dengan NPE yang dilemparkan oleh JVM itu sendiri. Dalam implementasi Java yang patuh, NPE yang dilempar oleh JVM memiliki
null
pesan. (Android Java berbeda.)sumber
Terlepas dari apa yang orang lain tunjukkan, ada baiknya memperhatikan peran kebaktian di sini. Dalam C #, misalnya, Anda juga memiliki konvensi yang sama untuk secara eksplisit meningkatkan pengecualian dalam kasus-kasus seperti ini, tetapi secara khusus
ArgumentNullException
, yang agak lebih spesifik. (C # konvensi adalah bahwaNullReferenceException
selalu merupakan bug dari beberapa jenis - cukup sederhana, seharusnya tidak pernah terjadi dalam kode produksi; diberikan,ArgumentNullException
biasanya tidak juga, tetapi bisa menjadi bug lebih sepanjang garis "Anda tidak mengerti bagaimana menggunakan perpustakaan dengan benar "jenis bug).Jadi, pada dasarnya, dalam C #
NullReferenceException
berarti bahwa program Anda benar-benar mencoba menggunakannya, sedangkanArgumentNullException
itu berarti bahwa ia mengakui bahwa nilainya salah dan bahkan tidak repot-repot untuk mencoba menggunakannya. Implikasinya sebenarnya bisa berbeda (tergantung pada keadaan) karenaArgumentNullException
berarti bahwa metode yang dimaksud belum memiliki efek samping (karena gagal dalam prasyarat metode).Secara kebetulan, jika Anda menaikkan sesuatu seperti
ArgumentNullException
atauIllegalArgumentException
, itulah bagian dari titik melakukan pemeriksaan: Anda menginginkan pengecualian yang berbeda dari yang biasanya Anda dapatkan.Either way, secara eksplisit meningkatkan pengecualian memperkuat praktik baik menjadi eksplisit tentang pra-kondisi metode Anda dan argumen yang diharapkan, yang membuat kode lebih mudah dibaca, digunakan, dan dipelihara. Jika Anda tidak secara eksplisit memeriksa
null
, saya tidak tahu apakah itu karena Anda berpikir bahwa tidak ada yang akan pernah melewatinull
argumen, Anda tetap menghitungnya untuk membuang pengecualian, atau Anda hanya lupa untuk memeriksa itu.sumber
null
objek lainnya." Saya tidak tergila-gila dengan itu, tapi itu tampaknya desain yang dimaksudkan.NullReferenceException
(setara denganNullPointerException
) berarti bahwa kode Anda benar-benar mencoba menggunakannya (yang selalu merupakan bug - seharusnya tidak pernah terjadi dalam kode produksi), vs. "Saya tahu argumennya salah, jadi saya bahkan tidak coba gunakan itu. " Ada jugaArgumentException
(yang berarti bahwa argumen itu salah karena alasan lain).Sehingga Anda akan mendapatkan pengecualian begitu Anda melakukan kesalahan, daripada nanti ketika Anda menggunakan peta dan tidak akan mengerti mengapa itu terjadi.
sumber
Ini mengubah kondisi kesalahan yang tampaknya tidak menentu menjadi pelanggaran kontrak yang jelas: Fungsi ini memiliki beberapa prasyarat untuk bekerja dengan benar, sehingga memeriksa mereka sebelumnya, memaksa mereka untuk dipenuhi.
Efeknya adalah, Anda tidak perlu melakukan debug
computeIfPresent()
ketika Anda mendapatkan pengecualian darinya. Setelah Anda melihat bahwa pengecualian tersebut berasal dari pemeriksaan prasyarat, Anda tahu bahwa Anda memanggil fungsi dengan argumen ilegal. Jika cek tidak ada di sana, Anda harus mengecualikan kemungkinan ada beberapa bug di dalamnyacomputeIfPresent()
yang mengarah ke pengecualian yang dilemparkan.Jelas, melempar
NullPointerException
obat generik adalah pilihan yang sangat buruk, karena tidak menandakan adanya pelanggaran kontrak.IllegalArgumentException
akan menjadi pilihan yang lebih baik.Sidenote:
Saya tidak tahu apakah Java mengizinkan ini (saya ragu), tetapi programmer C / C ++ menggunakan
assert()
dalam kasus ini, yang secara signifikan lebih baik untuk debugging: Ini memberitahu program untuk crash segera dan sekeras mungkin harus disediakan kondisi bernilai false. Jadi, jika Anda berlaridi bawah debugger, dan sesuatu masuk
NULL
ke salah satu argumen, program akan berhenti tepat di baris yang mengatakan argumen manaNULL
, dan Anda akan dapat memeriksa semua variabel lokal dari seluruh tumpukan panggilan di waktu luang.sumber
assert something != null;
tapi itu membutuhkan-assertions
bendera saat menjalankan aplikasi. Jika-assertions
bendera tidak ada di sana, kata kunci yang menegaskan tidak akan membuang AssertionExceptionItu karena itu mungkin tidak terjadi secara alami. Mari kita lihat sepotong kode seperti ini:
(tentu saja ada banyak masalah dalam sampel ini tentang kualitas kode. Maaf malas membayangkan sampel sempurna)
Situasi ketika
c
tidak akan benar-benar digunakan danNullPointerException
tidak akan dibuang bahkan jikac == null
memungkinkan.Dalam situasi yang lebih rumit, menjadi sangat tidak mudah untuk memburu kasus seperti itu. Inilah sebabnya mengapa pemeriksaan umum seperti
if (c == null) throw new NullPointerException()
lebih baik.sumber
Dimaksudkan untuk melindungi kerusakan lebih lanjut, atau memasuki kondisi yang tidak konsisten.
sumber
Terlepas dari semua jawaban bagus lainnya di sini, saya juga ingin menambahkan beberapa kasus.
Anda dapat menambahkan pesan jika Anda membuat pengecualian Anda sendiri
Jika Anda melempar sendiri,
NullPointerException
Anda dapat menambahkan pesan (yang memang seharusnya!)Pesan default adalah
null
darinew NullPointerException()
dan semua metode yang menggunakannya, misalnyaObjects.requireNonNull
. Jika Anda mencetak nol itu bahkan dapat menerjemahkan ke string kosong ...Agak pendek dan tidak informatif ...
Jejak tumpukan akan memberikan banyak informasi, tetapi bagi pengguna untuk mengetahui apa yang nol mereka harus menggali kode dan melihat baris yang tepat.
Sekarang bayangkan NPE dibungkus dan dikirim melalui internet, misalnya sebagai pesan dalam kesalahan layanan web, mungkin antara departemen yang berbeda atau bahkan organisasi. Skenario terburuk, tidak ada yang tahu apa
null
kepanjangan dari ...Panggilan metode berantai akan membuat Anda menebak
Pengecualian hanya akan memberi tahu Anda pada baris apa pengecualian itu terjadi. Pertimbangkan baris berikut:
Jika Anda mendapatkan NPE dan menunjuk pada baris ini, yang mana dari
repository
dansomeObject
nol?Sebaliknya, memeriksa variabel-variabel ini ketika Anda mendapatkannya setidaknya akan menunjuk ke baris di mana mereka diharapkan satu-satunya variabel yang ditangani. Dan, seperti yang disebutkan sebelumnya, bahkan lebih baik jika pesan kesalahan Anda berisi nama variabel atau sejenisnya.
Kesalahan saat memproses banyak input harus memberikan informasi pengenal
Bayangkan program Anda sedang memproses file input dengan ribuan baris dan tiba-tiba ada NullPointerException. Anda melihat tempat itu dan menyadari beberapa input salah ... input apa? Anda akan memerlukan informasi lebih lanjut tentang nomor baris, mungkin kolom atau bahkan seluruh teks baris untuk memahami baris apa dalam file yang perlu diperbaiki.
sumber