IllegalArgumentException atau NullPointerException untuk parameter nol? [Tutup]

547

Saya memiliki metode penyetel sederhana untuk properti dan nulltidak sesuai untuk properti khusus ini. Saya selalu terkoyak dalam situasi ini: haruskah saya melemparkan IllegalArgumentException, atau a NullPointerException? Dari javadocs, keduanya tampak tepat. Apakah ada semacam standar yang dipahami? Atau apakah ini hanya salah satu dari hal-hal yang harus Anda lakukan sesuka hati dan keduanya benar?

Mike Stone
sumber

Jawaban:

299

Sepertinya IllegalArgumentExceptiondipanggil untuk jika Anda tidak ingin nullmenjadi nilai yang diizinkan, dan NullPointerExceptionakan dilemparkan jika Anda mencoba menggunakan variabel yang ternyata null.

Greg Hurlman
sumber
33
Jawaban berikut untuk pertanyaan ini membuat argumen persuasif bahwa NullPointerException adalah pengecualian yang benar: stackoverflow.com/a/8160/372926 ; stackoverflow.com/a/8196334/372926 ; dan stackoverflow.com/a/6358/372926 .
SamStephens
2
IllegalArgumentExceptionbertentangan dengan Java's Objects.requireNonNull (T) dan Guava's Preconditions.checkNotNull (T) yang melempar a NullPointerException. Namun, jawaban yang benar secara definitelly IllegalArgumentException seperti dijelaskan dalam jawaban yang sangat baik Jason Cohen dan bagian komentar itu .
matoni
417

Anda harus menggunakan IllegalArgumentException(IAE), bukan NullPointerException(NPE) karena alasan berikut:

Pertama, NPE JavaDoc secara eksplisit mencantumkan kasus-kasus di mana NPE sesuai. Perhatikan bahwa mereka semua dilemparkan oleh runtime ketika nulldigunakan secara tidak tepat. Sebaliknya, IAE JavaDoc tidak bisa lebih jelas: "Dilemparkan untuk menunjukkan bahwa suatu metode telah melewati argumen ilegal atau tidak pantas." Yap, itu kamu!

Kedua, ketika Anda melihat NPE dalam jejak tumpukan, apa yang Anda asumsikan? Mungkin seseorang yang melakukan dereferensi null. Ketika Anda melihat IAE, Anda menganggap penelepon metode di bagian atas tumpukan melewati nilai ilegal. Sekali lagi, asumsi terakhir benar, yang pertama menyesatkan.

Ketiga, karena IAE jelas dirancang untuk memvalidasi parameter, Anda harus menganggapnya sebagai pilihan pengecualian standar, jadi mengapa Anda memilih NPE? Tentu saja tidak untuk perilaku yang berbeda - apakah Anda benar-benar mengharapkan kode panggilan untuk menangkap NPE secara terpisah dari IAE dan melakukan sesuatu yang berbeda sebagai hasilnya? Apakah Anda mencoba mengomunikasikan pesan kesalahan yang lebih spesifik? Tapi Anda bisa melakukannya dalam teks pesan pengecualian, seperti yang seharusnya Anda lakukan untuk semua parameter yang salah lainnya.

Keempat, semua data parameter yang salah lainnya adalah IAE, jadi mengapa tidak konsisten? Mengapa ilegal nullitu begitu istimewa sehingga pantas dikecualikan secara terpisah dari semua jenis argumen ilegal lainnya?

Akhirnya, saya menerima argumen yang diberikan oleh jawaban lain bahwa sebagian Java API menggunakan NPE dengan cara ini. Namun, Java API tidak konsisten dengan segala sesuatu mulai dari tipe pengecualian hingga konvensi penamaan, jadi saya pikir hanya menyalin secara membabi buta (bagian favorit Anda) Java API bukan argumen yang cukup baik untuk mengalahkan pertimbangan lain ini.

Jason Cohen
sumber
120
Java 2 Edisi Efektif, Butir 60: "Bisa dibilang, semua pemanggilan metode yang salah bermuara pada argumen ilegal atau keadaan ilegal, tetapi pengecualian lain secara standar digunakan untuk beberapa jenis argumen dan negara ilegal. Jika seorang penelepon memberikan nol pada beberapa parameter yang dengannya nilai null dilarang, konvensi menetapkan bahwa NullPointerException dilemparkan daripada IllegalArgumentException. Demikian pula, jika penelepon melewati nilai di luar kisaran dalam parameter yang mewakili indeks ke dalam urutan, IndexOutOfBoundsException harus dilemparkan daripada IllegalArgumentException. "
Gili
33
JavaDoc untuk NPE juga menyatakan sebagai berikut: "Aplikasi harus membuang instance dari kelas ini untuk menunjukkan penggunaan ilegal lain dari objek null." Yang ini bisa lebih jelas :(
R. Martinho Fernandes
12
Sayangnya, metode validasi Validate.notNull(commons lang) dan Preconditions.checkNotNull(jambu biji) keduanya melemparkan NPE :-(
Aaron Digulla
2
Meskipun Guava juga memiliki Preconditions.checkArgument () melempar IllegalArgumentException ...
michaelok
16
@AaronDigulla, dari dokumentasi Guava: "Kami menyadari ada banyak argumen yang valid untuk melemparkan IAE pada argumen nol. Bahkan, jika kami memiliki mesin waktu untuk kembali> 15 tahun, kami mungkin bahkan mencoba untuk mendorong hal-hal dalam Namun, kami telah memutuskan untuk tetap menggunakan JDK dan preferensi Java Efektif dari NullPointerException. Jika Anda teguh dalam keyakinan Anda bahwa IAE benar, Anda masih memiliki checkArgument(arg != null), hanya tanpa kenyamanan mengembalikan arg, atau Anda dapat membuat utilitas lokal untuk proyek Anda. " code.google.com/p/guava-libraries/wiki/IdeaGraveyard
Arto Bendiken
162

Standarnya adalah melempar NullPointerException. "Java Efektif" yang secara umum sempurna membahas hal ini secara singkat dalam Butir 42 (edisi pertama), Butir 60 (edisi kedua), atau Butir 72 (edisi ketiga) "Mendukung penggunaan pengecualian standar":

"Dapat diperdebatkan, semua pemanggilan metode yang salah bermuara pada argumen ilegal atau keadaan ilegal, tetapi pengecualian lain secara standar digunakan untuk jenis argumen dan negara ilegal tertentu. NullPointerException dibuang daripada IllegalArgumentException. "

GaryF
sumber
95
Saya sangat tidak setuju. NullPointerExceptions hanya boleh dibuang jika JVM secara tidak sengaja mengikuti referensi nol. Itulah perbedaan yang membantu Anda ketika dipanggil untuk melihat kode jam 3 pagi.
Thorbjørn Ravn Andersen
26
Saya tidak selalu setuju dengan standar (saya benar-benar bisa pergi dengan cara baik pada masalah ini), tapi itu adalah apa penggunaan standar di seluruh JDK, maka Java efektif membuat kasus untuk itu. Saya pikir ini adalah kasus memilih apakah akan mengikuti standar atau tidak, atau melakukan hal yang Anda rasa benar. Kecuali Anda memiliki alasan yang sangat bagus (dan ini tentu saja memenuhi syarat), yang terbaik adalah mengikuti praktik standar.
GaryF
21
Jejak pengecualian menunjukkan titik pengecualian, jadi jika perbedaan dalam jenis pengecualian menyebabkan neraka bagi Anda atau "perbedaan yang membantu Anda", Anda melakukan sesuatu yang sangat salah.
Jim Balter
8
Fantasi dan kecanggihan. Tepat di bawah, MB menulis "Perhatikan bahwa argumen tentang debugging keras adalah palsu karena Anda tentu saja dapat memberikan pesan kepada NullPointerException mengatakan apa yang nol dan mengapa itu tidak boleh nol. Sama seperti dengan IllegalArgumentException."
Jim Balter
10
Sebagai penjawab asli di sini, izinkan saya mengatakan itu tidak masalah. Tentu tidak menjamin 6 tahun percakapan. Pilih satu, mana saja yang Anda suka, dan konsistenlah. Standar, seperti yang saya tunjukkan, adalah NPE. Jika Anda lebih suka IAE untuk alasan apa pun, lakukanlah. Bersikaplah konsisten.
GaryF
138

Saya semua mendukung membuang IllegalArgumentExceptionparameter nol, sampai hari ini, ketika saya perhatikan java.util.Objects.requireNonNullmetode di Jawa 7. Dengan metode itu, alih-alih melakukan:

if (param == null) {
    throw new IllegalArgumentException("param cannot be null.");
}

Anda dapat melakukan:

Objects.requireNonNull(param);

dan itu akan melempar NullPointerExceptionjika parameter yang Anda lewati itu null.

Mengingat bahwa metode itu benar-benar tepat di tengah-tengah, java.utilsaya menganggap keberadaannya sebagai indikasi yang cukup kuat bahwa melempar NullPointerExceptionadalah "cara Jawa dalam melakukan sesuatu".

Saya pikir saya sudah memutuskan bagaimanapun juga.

Perhatikan bahwa argumen tentang debugging keras adalah palsu karena Anda tentu saja dapat memberikan pesan untuk NullPointerExceptionmengatakan apa yang nol dan mengapa itu tidak boleh nol. Sama seperti dengan IllegalArgumentException.

Salah satu keuntungan tambahan NullPointerExceptionadalah bahwa, dalam kode kritis berkinerja tinggi, Anda dapat membuang pemeriksaan eksplisit untuk null (dan NullPointerExceptiondengan pesan kesalahan ramah), dan hanya mengandalkan NullPointerExceptionAnda akan mendapatkan secara otomatis ketika Anda memanggil metode pada nol parameter. Asalkan Anda memanggil metode dengan cepat (yaitu gagal cepat), maka pada dasarnya Anda memiliki efek yang sama, hanya saja tidak cukup ramah pengguna untuk pengembang. Sering kali mungkin lebih baik untuk memeriksa secara eksplisit dan melempar dengan pesan yang berguna untuk menunjukkan parameter mana yang nol, tetapi senang memiliki opsi untuk mengubah bahwa jika kinerja menentukan tanpa melanggar kontrak yang diterbitkan dari metode / konstruktor.

MB.
sumber
14
Jambu biji Preconditions.checkNotNull(arg)juga melempar NPE.
assylias
14
Ini tidak benar-benar menambahkan bobot lebih ke NullPointerException untuk ARGUMEN nol ilegal. Baik JDK requireNonNull () dan Guava checkNotNull () dapat dipanggil di mana saja dalam kode, dengan objek apa pun. Anda bisa memanggil mereka di dalam lingkaran misalnya. requireNotNull () dan checkNotNull () tidak mungkin berasumsi untuk dipanggil dengan beberapa argumen metode dan melempar IllegalArgumentException. Perhatikan bahwa Guava juga memiliki Preconditions.checkArgument () yang melempar IllegalArgumentException.
Bogdan Calmac
2
Suatu poin yang adil oleh Bogdan, meskipun saya curiga penggunaan yang umum (dan umumnya dimaksudkan) dari requireNonNull adalah untuk memeriksa argumen. Jika Anda perlu memeriksa bahwa sesuatu tidak nol dalam satu lingkaran saya akan berpikir pernyataan akan menjadi cara yang khas.
MB.
15
Saya pikir JavaDoc dari Object.requireNonNull () menambah bobot argumen: "Metode ini dirancang terutama untuk melakukan validasi parameter dalam metode dan konstruktor"
Richard Zschech
Perlu diingat bahwa argumen kinerja yang mendukung pemeriksaan nol implisit seringkali tidak valid. JIT dapat mengenali cek kosong pengguna dan menghapus cek kosong tersirat berikut, dan / atau menggunakan set optimisasi yang sama pada kedua jenis cek kosong. Lihat wiki ini untuk info lebih lanjut, khususnya: Pemeriksaan nol yang ditulis pengguna dalam banyak kasus identik secara fungsional dengan yang dimasukkan oleh JVM. Tentu saja, jika Anda melakukan sesuatu yang lebih bagus di null Anda seperti pesan khusus, pengoptimalan ini mungkin tidak berlaku.
BeeOnRope
70

Saya cenderung mengikuti desain perpustakaan JDK, terutama Collections and Concurrency (Joshua Bloch, Doug Lea, mereka tahu cara mendesain API yang solid). Bagaimanapun, banyak API di JDK secara proaktif melempar NullPointerException.

Misalnya, Javadoc untuk Map.containsKeynegara:

@throws NullPointerException jika kuncinya adalah nol dan peta ini tidak mengizinkan kunci null (opsional).

Sangat valid untuk melempar NPE Anda sendiri. Konvensi ini adalah untuk memasukkan nama parameter yang nol dalam pesan pengecualian.

Polanya berbunyi:

public void someMethod(Object mustNotBeNull) {  
    if (mustNotBeNull == null) {  
        throw new NullPointerException("mustNotBeNull must not be null");  
    }  
}

Apa pun yang Anda lakukan, jangan biarkan nilai buruk ditetapkan dan melempar pengecualian nanti ketika kode lain mencoba menggunakannya. Itu membuat debugging menjadi mimpi buruk. Anda harus selalu mengikuti prinsip "gagal-cepat".

Mark Renouf
sumber
3
Makanan untuk dipikirkan: Mungkin alasan bahwa NullPointerException tidak memperpanjang IllegalArgumentException adalah bahwa yang pertama dapat terjadi dalam kasus-kasus yang tidak melibatkan argumen metode.
Gili
2
@Gili: mungkin masalah ini ada di tempat pertama karena Java tidak mendukung multiple inheritance. Jika Java mendukung MI, Anda dapat melempar pengecualian yang mewarisi dari kedua IllegalArgumentException dan NullPointerException.
Lie Ryan
7
Pesan tidak perlu menyertakan argumen, karena akan selalu nol, memberikan: "null tidak boleh nol", tidak terlalu berguna. :-) Kalau tidak, saya setuju, Anda dapat membuat NPE "kaya", dengan pesan yang bermakna.
PhiLho
5
Saya harus setuju - ikuti API standar jika ragu. Tidak semua yang ada di API adalah pikiran Anda yang optimal, tetapi tetap dipertahankan dan diulangi oleh ratusan pengembang, dan digunakan oleh jutaan programmer. Sekarang di Java 7 kita memiliki contoh lain dari NPE yang digunakan dengan cara ini; metode Objects.requireNonNull (T obj) - secara jelas ditentukan untuk memeriksa bahwa referensi objek tidak nol, secara jelas ditentukan untuk melakukan validasi parameter dalam metode / konstruktor, dan secara jelas ditentukan untuk melempar NPE jika objek tersebut nol. Akhir dari
flamming_python
44

Memilih argumen Jason Cohen karena disajikan dengan baik. Biarkan saya menguraikannya langkah demi langkah. ;-)

  • Itu NPE javadoc secara eksplisit mengatakan, "penggunaan ilegal lain dari objek null" . Jika itu hanya terbatas pada situasi di mana runtime menemukan nol ketika seharusnya tidak, semua kasus seperti itu dapat didefinisikan jauh lebih ringkas.

  • Tidak dapat membantu jika Anda mengasumsikan hal yang salah, tetapi dengan asumsi enkapsulasi diterapkan dengan benar, Anda benar-benar tidak boleh peduli atau memperhatikan apakah null dereferensi secara tidak tepat vs apakah suatu metode mendeteksi nol yang tidak pantas dan mengeluarkan pengecualian.

  • Aku akan memilih NPE atas IAE karena berbagai alasan

    • Ini lebih spesifik tentang sifat operasi ilegal
    • Logika yang keliru memungkinkan null cenderung sangat berbeda dari logika yang keliru memungkinkan nilai ilegal. Misalnya, jika saya memvalidasi data yang dimasukkan oleh pengguna, jika saya mendapatkan nilai yang tidak dapat diterima, sumber kesalahan itu adalah dengan pengguna akhir aplikasi. Jika saya mendapatkan nol, itu kesalahan programmer.
    • Nilai-nilai yang tidak valid dapat menyebabkan hal-hal seperti stack overflows, out of memory error, parsing exception, dll. Memang, sebagian besar kesalahan umumnya hadir, pada beberapa titik, sebagai nilai yang tidak valid dalam beberapa pemanggilan metode. Untuk alasan ini saya melihat IAE sebagai yang paling umum dari semua pengecualian di bawah RuntimeException.
  • Sebenarnya, argumen tidak valid lainnya dapat menghasilkan semua jenis pengecualian lainnya. UnknownHostException , FileNotFoundException , berbagai pengecualian kesalahan sintaksis, IndexOutOfBoundsException , kegagalan otentikasi, dll., Dll.

Secara umum, saya merasa NPE banyak difitnah karena secara tradisional telah dikaitkan dengan kode yang gagal mengikuti prinsip gagal cepat . Itu, ditambah kegagalan JDK untuk mengisi NPE dengan string pesan benar-benar telah menciptakan sentimen negatif yang kuat yang tidak beralasan. Memang, perbedaan antara NPE dan IAE dari perspektif runtime adalah namanya. Dari perspektif itu, semakin tepat Anda dengan nama, semakin jelas Anda memberi kepada penelepon.

Christopher Smith
sumber
Perbedaan antara sebagian besar pengecualian yang tidak dicentang hanyalah namanya.
Thorbjørn Ravn Andersen
20

Ini pertanyaan gaya "Perang Suci". Dengan kata lain, kedua alternatif itu baik, tetapi orang akan memiliki preferensi mereka yang akan mereka pertahankan sampai mati.

Steve McLeod
sumber
Tidak, Hanya ada satu jawaban yang benar untuk pertanyaan ini dan itu adalah dengan menggunakan pengecualian melempar IllegalArgument ketika input ke metode salah. Juga di lingkungan dev, Anda dapat menggunakan pernyataan untuk memeriksa validitas input dan melemparkan pengecualian yang tepat;)
Mr.Q
@ Mr.Q Dan saya pikir itu NullPointerExceptionharus dilemparkan: ini adalah konvensi yang digunakan JDK, dan memerlukan untuk antarmuka, itu lebih spesifik (seperti IndexOutOfBoundsException, dll.), Dll.
Solomon Ucko
kekekekkek ...: D
Yousha Aleayoub
17

Jika ini adalah settermetode dan nullsedang diteruskan, saya pikir akan lebih masuk akal untuk melempar IllegalArgumentException. A NullPointerExceptiontampaknya lebih masuk akal jika Anda mencoba untuk benar-benar menggunakannull .

Jadi, jika Anda menggunakannya dan itu null, NullPointer. Jika itu yang lulus dan itu null, IllegalArgument.

Jeremy Privett
sumber
9

Apache Commons Lang memiliki NullArgumentException yang melakukan sejumlah hal yang dibahas di sini: ia memperluas IllegalArgumentException dan konstruktor satu-satunya mengambil nama argumen yang seharusnya bukan nol.

Sementara saya merasa bahwa melempar sesuatu seperti NullArgumentException atau IllegalArgumentException lebih akurat menggambarkan keadaan luar biasa, kolega saya dan saya telah memilih untuk tunduk pada saran Bloch tentang masalah tersebut.

Brian T. Grant
sumber
7
Perhatikan mereka menghapusnya dari commons-lang3: apache-commons.680414.n4.nabble.com/…
artbristol
7

Tidak bisa setuju dengan apa yang dikatakan. Gagal lebih awal, gagal cepat. Mantra Pengecualian yang cukup bagus.

Pertanyaan tentang Pengecualian yang harus dilontarkan sebagian besar adalah masalah selera pribadi. Dalam pikiran saya IllegalArgumentException tampaknya lebih spesifik daripada menggunakan NPE karena ini memberi tahu saya bahwa masalahnya adalah dengan argumen yang saya sampaikan ke metode dan bukan dengan nilai yang mungkin telah dihasilkan saat melakukan metode.

2 Cents saya

Allain Lalonde
sumber
7

Sebenarnya, pertanyaan melempar IllegalArgumentException atau NullPointerException dalam pandangan saya yang sederhana hanyalah "perang suci" bagi minoritas dengan pemahaman yang tidak lengkap tentang penanganan pengecualian di Jawa. Secara umum, aturannya sederhana, dan sebagai berikut:

  • pelanggaran batasan argumen harus ditunjukkan secepat mungkin (-> gagal cepat), untuk menghindari keadaan ilegal yang jauh lebih sulit untuk di-debug
  • dalam hal penunjuk null tidak valid untuk alasan apa pun, lempar NullPointerException
  • dalam hal indeks array / koleksi ilegal, lemparkan ArrayIndexOutOfBounds
  • dalam hal ukuran array / koleksi negatif, lempar NegativeArraySizeException
  • dalam kasus argumen ilegal yang tidak tercakup oleh hal di atas, dan yang Anda tidak memiliki jenis pengecualian lain yang lebih spesifik, buang IllegalArgumentException sebagai keranjang sampah
  • di sisi lain, dalam kasus pelanggaran kendala DALAM BIDANG yang tidak dapat dihindari dengan kegagalan cepat karena beberapa alasan yang sah, tangkap dan rethrow sebagai IllegalStateException atau pengecualian yang diperiksa lebih spesifik. Jangan pernah melewatkan NullPointerException asli, ArrayIndexOutOfBounds, dll dalam hal ini!

Setidaknya ada tiga alasan yang sangat baik terhadap kasus pemetaan semua jenis pelanggaran batasan argumen untuk IllegalArgumentException, dengan yang ketiga mungkin sangat parah untuk menandai praktik gaya buruk:

(1) Seorang programmer tidak dapat dengan aman berasumsi bahwa semua kasus pelanggaran batasan argumen mengakibatkan IllegalArgumentException, karena sebagian besar kelas standar menggunakan pengecualian ini sebagai keranjang sampah jika tidak ada jenis pengecualian yang lebih spesifik yang tersedia. Mencoba memetakan semua kasus pelanggaran batasan argumen untuk IllegalArgumentException di API Anda hanya menyebabkan frustrasi pemrogram menggunakan kelas Anda, karena pustaka standar kebanyakan mengikuti aturan berbeda yang melanggar Anda, dan sebagian besar pengguna API Anda akan menggunakannya juga!

(2) Memetakan pengecualian sebenarnya menghasilkan anomali yang berbeda, yang disebabkan oleh pewarisan tunggal: Semua pengecualian Jawa adalah kelas, dan karenanya hanya mendukung warisan tunggal. Oleh karena itu, tidak ada cara untuk membuat pengecualian yang benar-benar mengatakan NullPointerException dan IllegalArgumentException, karena subclass hanya dapat mewarisi dari satu atau yang lain. Melempar IllegalArgumentException dalam kasus argumen nol karenanya mempersulit pengguna API untuk membedakan antara masalah setiap kali sebuah program mencoba untuk memperbaiki masalah secara terprogram, misalnya dengan memasukkan nilai default ke dalam pengulangan panggilan!

(3) Pemetaan benar-benar menciptakan bahaya penyembunyian bug: Untuk memetakan pelanggaran batasan argumen ke IllegalArgumentException, Anda harus membuat kode uji coba luar dalam setiap metode yang memiliki argumen terbatas. Namun, hanya menangkap RuntimeException di blok tangkap ini adalah keluar dari pertanyaan, karena pemetaan risiko mendokumentasikan RuntimeException dilemparkan oleh metode bebas yang digunakan di dalam Anda ke IllegalArgumentException, bahkan jika mereka tidak disebabkan oleh pelanggaran batasan argumen. Jadi Anda harus sangat spesifik, tetapi bahkan upaya itu tidak melindungi Anda dari kasus bahwa Anda secara tidak sengaja memetakan pengecualian runtime tidak berdokumen dari API lain (yaitu bug) ke IllegalArgumentException API Anda.

Dengan praktik standar di sisi lain, aturan tetap sederhana, dan pengecualian menyebabkan tetap membuka kedok dan spesifik. Untuk pemanggil metode, aturannya juga mudah: - jika Anda menemukan pengecualian runtime yang didokumentasikan dalam bentuk apa pun karena Anda melewati nilai ilegal, ulangi panggilan dengan default (untuk pengecualian khusus ini diperlukan), atau perbaiki kode Anda - jika di sisi lain Anda menemukan pengecualian runtime yang tidak didokumentasikan terjadi untuk set argumen tertentu, ajukan laporan bug ke pembuat metode untuk memastikan bahwa kode mereka atau dokumentasinya diperbaiki.

Sascha Baumeister
sumber
6

Praktek yang diterima jika menggunakan IllegalArgumentException (pesan String) untuk mendeklarasikan parameter menjadi tidak valid dan memberikan detail sebanyak mungkin ... Jadi untuk mengatakan bahwa parameter ditemukan nol sementara pengecualian non-nol, Anda akan melakukan sesuatu seperti ini:

if( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");

Anda hampir tidak memiliki alasan untuk secara implisit menggunakan "NullPointerException". NullPointerException adalah pengecualian yang dilemparkan oleh Java Virtual Machine ketika Anda mencoba mengeksekusi kode pada referensi nol (Suka toString () ).

Claude Houle
sumber
6

Melempar pengecualian yang eksklusif untuk nullargumen (apakah NullPointerExceptionatau jenis khusus) membuat nullpengujian otomatis lebih dapat diandalkan. Pengujian otomatis ini dapat dilakukan dengan refleksi dan seperangkat nilai default, seperti pada Guava 's NullPointerTester. Misalnya, NullPointerTesterakan mencoba memanggil metode berikut ...

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}

... dengan dua daftar argumen: "", nulldan null, ImmutableList.of(). Ini akan menguji bahwa masing-masing panggilan ini melempar yang diharapkan NullPointerException. Untuk implementasi ini, melewati nulldaftar tidak menghasilkan NullPointerException. Itu, bagaimanapun, kebetulan menghasilkan IllegalArgumentExceptionkarena NullPointerTesterkebetulan menggunakan string default "". Jika NullPointerTestermengharapkan hanya NullPointerExceptionuntuk nullnilai, itu akan menangkap bug. Jika mengharapkan IllegalArgumentException, itu meleset.

Chris Povirk
sumber
5

Beberapa koleksi menganggap bahwa nullditolak menggunakan NullPointerExceptionbukan IllegalArgumentException. Misalnya, jika Anda membandingkan satu set yang berisi nullsatu set yang menolak null, set pertama akan memanggil containsAllyang lain dan menangkap NullPointerException- tetapi tidak IllegalArgumentException. (Saya sedang melihat implementasi dari AbstractSet.equals.)

Anda dapat berargumen dengan masuk akal bahwa menggunakan pengecualian yang tidak dicentang dengan cara ini adalah antipattern, bahwa membandingkan koleksi yang mengandung nullkoleksi yang tidak dapat mengandung nullkemungkinan bug yang benar-benar harus menghasilkan pengecualian, atau menempatkan nullkoleksi sama sekali adalah ide yang buruk . Namun demikian, kecuali jika Anda bersedia mengatakan bahwa equalsharus mengeluarkan pengecualian dalam kasus seperti itu, Anda terjebak mengingat yang NullPointerExceptiondiperlukan dalam keadaan tertentu tetapi tidak pada orang lain. ("IAE sebelum NPE kecuali setelah 'c' ...")

Chris Povirk
sumber
Saya tidak melihat bagaimana konten melempar NPE tergantung pada elemen di dalam koleksi. Satu-satunya alasan NPE terlempar (afaict) adalah jika koleksi itu sendiri adalah nol (dalam hal ini NPE dilemparkan karena mencoba mengakses iteratornya). Namun ini menimbulkan pertanyaan apakah input nol harus diperiksa atau jika harus disebarkan sampai dicoba diakses.
Alowaniak
2
new TreeSet<>().containsAll(Arrays.asList((Object) null));melempar NPEkarena Listmengandung null.
Chris Povirk
1
Ah memang, TreeSet # berisi throws NPE "jika elemen yang ditentukan adalah null dan set ini menggunakan pemesanan alami, atau pembandingnya tidak mengizinkan elemen null". Hanya melihat AbstractSet yang memungkinkan null, salah saya. Secara pribadi saya merasa aneh itu tidak hanya mengembalikan false, karena dalam kasus itu tidak mungkin ada null yang ditambahkan.
Alowaniak
5

Sebagai pertanyaan subjektif, ini harus ditutup, tetapi karena masih terbuka:

Ini adalah bagian dari kebijakan internal yang digunakan di tempat kerja saya sebelumnya dan itu bekerja dengan sangat baik. Ini semua dari ingatan jadi saya tidak dapat mengingat kata-kata yang tepat. Perlu dicatat bahwa mereka tidak menggunakan pengecualian yang diperiksa, tetapi itu berada di luar cakupan pertanyaan. Pengecualian yang tidak dicentang yang mereka gunakan jatuh ke dalam 3 kategori utama.

NullPointerException: Jangan melempar dengan sengaja. NPE harus dibuang hanya oleh VM ketika referensi referensi nol. Semua upaya yang mungkin harus dilakukan untuk memastikan bahwa ini tidak pernah dibuang. @Nullable dan @NotNull harus digunakan bersama dengan alat analisis kode untuk menemukan kesalahan ini.

IllegalArgumentException: Dilemparkan ketika argumen ke suatu fungsi tidak sesuai dengan dokumentasi publik, sehingga kesalahan dapat diidentifikasi dan dijelaskan dalam hal argumen yang diteruskan. Situasi OP akan jatuh ke dalam kategori ini.

IllegalStateException: Dilemparkan ketika suatu fungsi dipanggil dan argumennya tidak terduga pada saat dilewatkan atau tidak sesuai dengan keadaan objek yang digunakan oleh metode tersebut.

Misalnya, ada dua versi internal dari IndexOutOfBoundsException yang digunakan dalam hal-hal yang memiliki panjang. Satu sub-kelas IllegalStateException, digunakan jika indeks lebih besar dari panjangnya. Subclass lain dari IllegalArgumentException, digunakan jika indeksnya negatif. Ini karena Anda bisa menambahkan lebih banyak item ke objek dan argumennya akan valid, sementara angka negatif tidak pernah valid.

Seperti yang saya katakan, sistem ini bekerja dengan sangat baik, dan butuh seseorang untuk menjelaskan mengapa perbedaan itu ada: "Tergantung pada jenis kesalahan itu cukup mudah bagi Anda untuk mencari tahu apa yang harus dilakukan. Bahkan jika Anda tidak dapat benar-benar mencari tahu mencari tahu apa yang salah Anda bisa mencari tahu di mana untuk menangkap kesalahan itu dan membuat informasi debugging tambahan. "

NullPointerException: Tangani case Null atau masukkan pernyataan agar NPE tidak terlempar. Jika Anda memasukkan pernyataan hanya satu dari dua jenis lainnya. Jika memungkinkan, lanjutkan men-debug seolah-olah pernyataan itu ada di sana.

IllegalArgumentException: Anda memiliki sesuatu yang salah di situs panggilan Anda. Jika nilai yang diteruskan berasal dari fungsi lain, cari tahu mengapa Anda menerima nilai yang salah. Jika Anda memasukkan salah satu argumen Anda menyebarkan kesalahan memeriksa tumpukan panggilan sampai Anda menemukan fungsi yang tidak mengembalikan apa yang Anda harapkan.

IllegalStateException: Anda belum memanggil fungsi Anda dalam urutan yang benar. Jika Anda menggunakan salah satu argumen Anda, periksa dan buang IllegalArgumentException yang menjelaskan masalah tersebut. Anda kemudian dapat merambatkan pipi ke atas tumpukan sampai Anda menemukan masalah.

Ngomong-ngomong, maksudnya adalah Anda hanya bisa menyalin IllegalArgumentAssertions ke tumpukan. Tidak ada cara bagi Anda untuk menyebarkan IllegalStateExceptions atau NullPointerExceptions ke atas tumpukan karena mereka ada hubungannya dengan fungsi Anda.

Ben Seidel
sumber
4

Secara umum, pengembang tidak boleh melempar NullPointerException. Pengecualian ini dilemparkan oleh runtime ketika kode mencoba untuk melakukan dereferensi variabel yang nilainya nol. Oleh karena itu, jika metode Anda ingin secara eksplisit melarang nol, sebagai kebalikan dari kebetulan memiliki nilai nol menaikkan NullPointerException, Anda harus melempar IllegalArgumentException.


sumber
9
JavaDoc pada NPE memiliki pendapat lain: "Aplikasi harus melempar instance dari kelas ini untuk menunjukkan penggunaan ilegal lain dari objek null." Jangan terlalu kategoris
Donz
4

Saya ingin memilih argumen Null dari argumen ilegal lainnya, jadi saya mendapatkan pengecualian dari IAE bernama NullArgumentException. Tanpa perlu membaca pesan pengecualian, saya tahu bahwa argumen nol diteruskan ke metode dan dengan membaca pesan, saya mencari tahu argumen mana yang nol. Saya masih menangkap NullArgumentException dengan pengendali IAE, tetapi di log saya adalah tempat saya bisa melihat perbedaannya dengan cepat.

Jason Fritcher
sumber
Saya telah mengadopsi pendekatan "melempar IllegalArgumentException (" foo == null ")" yang baru. Anda harus tetap mencatat nama variabel (untuk memastikan bahwa Anda melihat pernyataan yang benar, dll)
Thorbjørn Ravn Andersen
4

dikotomi ... Apakah mereka tidak tumpang tindih? Hanya bagian yang tidak tumpang tindih dari keseluruhan yang dapat membuat dikotomi. Seperti yang saya lihat:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));
Luis Daniel Mesa Velasquez
sumber
1
Ini akan menggandakan overhead untuk pembuatan pengecualian dan tidak akan membantu karena menangkap NullPointerExceptiontidak akan melakukan apa pun. Satu-satunya hal yang bisa membantu adalah IllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException, tetapi kami tidak memiliki banyak warisan.
maaartinus
Saya percaya bahwa pengecualian yang lebih spesifik harus dibungkus dengan pengecualian yang lebih umum. NPE adalah untuk ekspresi sedangkan IAE adalah untuk suatu metode. Karena metode berisi pernyataan yang mengandung ekspresi, IAE lebih umum.
Sgene9
Mengenai overhead, saya tidak tahu. Tetapi karena stacktraces pada dasarnya akan identik kecuali bahwa nama pengecualian berubah di tengah, saya pikir seharusnya tidak ada terlalu banyak overhead untuk pengecualian ganda. Jika seseorang khawatir tentang overhead, ia dapat menggunakan pernyataan "jika" untuk mengembalikan nol atau -1 alih-alih melemparkan pengecualian.
Sgene9
4

NullPointerExceptiondilemparkan ketika mencoba mengakses objek dengan variabel referensi yang nilainya saat ini null.

IllegalArgumentException dilemparkan ketika metode menerima argumen yang diformat berbeda dari metode yang diharapkan.

Nitesh Soomani
sumber
3

Menurut skenario Anda, IllegalArgumentExceptionadalah pilihan terbaik, karena nullbukan nilai yang valid untuk properti Anda.

Leo
sumber
0

Pengecualian runtime idealnya tidak harus dibuang. Pengecualian yang dicentang (pengecualian bisnis) harus dibuat untuk skenario Anda. Karena jika salah satu dari pengecualian ini dilemparkan dan dicatat, ini akan menyesatkan pengembang saat menelusuri log. Sebaliknya, pengecualian bisnis tidak membuat kepanikan itu dan biasanya diabaikan saat memecahkan masalah log.

vijay
sumber
-1

Definisi dari tautan ke dua pengecualian di atas adalah IllegalArgumentException: Dilemparkan untuk menunjukkan bahwa suatu metode telah melewati argumen ilegal atau tidak pantas. NullPointerException: Dilemparkan ketika aplikasi mencoba menggunakan null dalam kasus di mana objek diperlukan.

Perbedaan besar di sini adalah IllegalArgumentException yang seharusnya digunakan ketika memeriksa bahwa argumen ke suatu metode valid. NullPointerException seharusnya digunakan setiap kali suatu objek "digunakan" saat itu adalah nol.

Saya harap ini membantu menempatkan keduanya dalam perspektif.

martinatime
sumber
1
Bit yang menonjol adalah bahwa itu adalah aplikasi yang menggunakan null, bukan runtime. Jadi ada tumpang tindih yang cukup besar antara "ketika suatu metode telah melewati argumen ilegal atau tidak pantas" dan "ketika suatu aplikasi menggunakan null". Secara teori, jika aplikasi melewati nol untuk bidang yang membutuhkan non-nol, kedua kriteria dipenuhi.
Christopher Smith
-1

Jika itu "setter", atau di suatu tempat saya mendapatkan anggota untuk digunakan nanti, saya cenderung menggunakan IllegalArgumentException.

Jika itu sesuatu yang akan saya gunakan (dereference) sekarang dalam metode, saya melempar NullPointerException secara proaktif. Saya suka ini lebih baik daripada membiarkan runtime melakukannya, karena saya dapat memberikan pesan bermanfaat (sepertinya runtime bisa melakukan ini juga, tapi itu kata-kata kasar untuk hari lain).

Jika saya mengganti metode, saya menggunakan apa pun yang menggunakan metode ditimpa.

erickson
sumber
-1

Anda harus melempar IllegalArgumentException, karena akan membuat jelas bagi programmer bahwa ia telah melakukan sesuatu yang tidak valid. Pengembang sangat terbiasa melihat NPE dilemparkan oleh VM, sehingga setiap programmer tidak akan segera menyadari kesalahannya, dan akan mulai melihat-lihat secara acak, atau lebih buruk lagi, menyalahkan kode Anda karena 'buggy'.

Will Sargent
sumber
4
Maaf, jika seorang programmer melihat-lihat "secara acak" setelah mendapatkan segala jenis pengecualian ... mengubah nama pengecualian tidak akan banyak membantu.
Christopher Smith
-1

Dalam hal ini, IllegalArgumentException menyampaikan informasi yang jelas kepada pengguna menggunakan API Anda bahwa "tidak boleh nol". Seperti yang ditunjukkan oleh pengguna forum lain, Anda dapat menggunakan NPE jika Anda ingin selama Anda menyampaikan informasi yang benar kepada pengguna menggunakan API Anda.

GaryF dan tweakt menjatuhkan referensi "Java Efektif" (yang saya bersumpah) yang merekomendasikan penggunaan NPE. Dan melihat bagaimana API baik lainnya dibangun adalah cara terbaik untuk melihat bagaimana membangun API Anda.

Contoh bagus lainnya adalah dengan melihat API Musim Semi. Misalnya, org.springframework.beans.BeanUtils.instantiateClass (Constructor ctor, Object [] args) memiliki garis Assert.notNull (ctor, "Constructor must not null"). org.springframework.util.Assert.notNull (objek objek, pesan String) metode memeriksa untuk melihat apakah argumen (objek) yang dikirimkan adalah nol dan jika itu melempar IllegalArgumentException (pesan) baru yang kemudian ditangkap dalam org. springframework.beans.BeanUtils.instantiateClass (...) metode.


sumber
-5

Jika Anda memilih untuk melempar NPE dan Anda menggunakan argumen dalam metode Anda, mungkin berlebihan dan mahal untuk secara eksplisit memeriksa nol. Saya pikir VM sudah melakukan itu untuk Anda.

jassuncao
sumber
Waktu berjalan tidak akan menyertakan pesan yang bermakna.
mP.
Sebenarnya komentar ini bisa mendapatkan pendapat lain. Biarkan VM berbicara NPEtetapi programmer berbicara IAEsebelum VM jika mereka mau.
Jin Kwon
1
Mahal? Saya tidak berpikir bahwa == null itu mahal ... Selain itu, argumen nol dapat disimpan hanya untuk penggunaan terakhir, dan akan membuang pengecualian lama setelah pemanggilan metode, membuat kesalahan lebih sulit untuk dilacak. Atau Anda bisa membuat objek mahal sebelum menggunakan argumen nol, dan sebagainya. Deteksi dini tampaknya menjadi pilihan yang baik.
PhiLho
down voting dari jawaban ini adalah kesalahpahaman tentang perangkat keras di belakang. Tentu saja pemeriksaan perangkat keras (yang dilakukan CPU) lebih murah daripada pemeriksaan eksplisit. Dereferencing dari nol adalah kasus khusus SegmentationFault (SegV) (akses ke halaman yang tidak dimiliki oleh proses) yang CPU / OS periksa dan JRE menangani sebagai kasus khusus melempar NullPointerException.
digital_infinity