Pertanyaan Kapan seharusnya IllegalArgumentException dilemparkan?


Saya khawatir ini adalah pengecualian waktu proses sehingga mungkin harus digunakan dengan hemat.
Kasus penggunaan standar:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Tapi sepertinya itu akan memaksa desain berikut:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

Untuk mendapatkannya kembali menjadi pengecualian yang dicentang.

Oke, tapi ayo pergi dengan itu. Jika Anda memberi masukan yang buruk, Anda mendapatkan error runtime. Jadi, pertama-tama itu adalah kebijakan yang cukup sulit untuk diterapkan secara seragam, karena Anda dapat melakukan konversi yang sangat berlawanan:

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

Dan lebih buruk - saat memeriksa 0 <= pct && pct <= 100 kode klien dapat diharapkan untuk dilakukan secara statis, ini tidak demikian untuk data yang lebih maju seperti alamat email, atau lebih buruk lagi, sesuatu yang harus diperiksa terhadap database, oleh karena itu dalam kode klien umum tidak dapat melakukan pra-validasi.

Jadi pada dasarnya apa yang saya katakan adalah saya tidak melihat kebijakan konsisten yang berarti untuk digunakan IllegalArgumentException. Tampaknya itu tidak boleh digunakan dan kita harus tetap berpegang pada pengecualian kami sendiri. Apa kasus penggunaan yang baik untuk membuang ini?


76
2018-03-04 18:34


asal


Jawaban:


Dokumen api untuk IllegalArgumentException adalah:

Dilemparkan untuk menunjukkan bahwa suatu metode telah melewati argumen yang tidak sah atau tidak pantas.

Dari melihat bagaimana itu digunakan di perpustakaan jdk, Saya akan mengatakan:

  • Sepertinya langkah defensif untuk mengeluh tentang input yang jelas buruk sebelum input dapat masuk ke dalam karya dan menyebabkan sesuatu gagal setengah dengan pesan kesalahan masuk akal.

  • Ini digunakan untuk kasus-kasus di mana akan terlalu menjengkelkan untuk membuang pengecualian yang dicentang (meskipun itu membuat tampilan dalam kode java.lang.reflect, di mana kekhawatiran tentang tingkat yang konyol dari pengecualian-pengecualian-melempar tidak jelas).

Saya akan menggunakan IllegalArgumentException untuk melakukan pemeriksaan argumen defensif terakhir untuk utilitas umum (mencoba untuk tetap konsisten dengan penggunaan jdk), di mana harapannya adalah bahwa argumen buruk adalah kesalahan programmer, mirip dengan NPE. Saya tidak akan menggunakannya untuk menerapkan validasi dalam kode bisnis. Saya pasti tidak akan menggunakannya untuk contoh email.


57
2018-03-04 19:48



Ketika berbicara tentang "masukan buruk", Anda harus mempertimbangkan dari mana masukan itu berasal.

Apakah masukan dimasukkan oleh pengguna atau sistem eksternal lain yang tidak Anda kendalikan, Anda harus mengharapkan masukan menjadi tidak valid, dan selalu memvalidasinya. Sangat baik untuk mengecek pengecualian dalam kasus ini. Aplikasi Anda harus 'memulihkan' dari pengecualian ini dengan memberikan pesan kesalahan kepada pengguna.

Jika input berasal dari sistem Anda sendiri, mis. database Anda, atau beberapa bagian lain dari aplikasi Anda, Anda harus dapat mengandalkannya untuk menjadi valid (seharusnya sudah divalidasi sebelum sampai di sana). Dalam kasus ini, sangat baik untuk membuang pengecualian yang tidak terkendali seperti IllegalArgumentException, yang tidak boleh tertangkap (secara umum Anda tidak boleh menangkap pengecualian yang tidak terkendali). Ini adalah kesalahan programmer bahwa nilai yang tidak sah ada di sana di tempat pertama;) Anda harus memperbaikinya.


19
2018-03-04 20:06



Melemparkan pengecualian runtime "hemat" bukanlah kebijakan yang baik - Java yang efektif menyarankan Anda menggunakan pengecualian yang dicentang saat pemanggil dapat diharapkan untuk pulih. (Kesalahan programmer adalah contoh spesifik: jika kasus tertentu menunjukkan kesalahan programmer, maka Anda harus membuang pengecualian yang tidak dicentang; Anda ingin programmer memiliki setumpuk jejak di mana masalah logika terjadi, tidak mencoba untuk menangani sendiri.)

Jika tidak ada harapan untuk sembuh, maka jangan ragu untuk menggunakan pengecualian yang tidak terkendali; tidak ada gunanya menangkap mereka, jadi itu baik-baik saja.

Ini tidak 100% jelas dari contoh Anda yang mana contoh ini ada dalam kode Anda.


10
2018-03-04 18:40



Setiap API harus memeriksa validitas setiap parameter dari setiap metode publik sebelum mengeksekusinya:

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

Mereka mewakili 99,9% dari kesalahan waktu dalam aplikasi karena meminta operasi yang tidak mungkin sehingga pada akhirnya mereka adalah bug yang harus crash aplikasi (sehingga merupakan kesalahan yang tidak dapat dipulihkan).

Dalam hal ini dan mengikuti pendekatan gagal dengan cepat Anda harus membiarkan aplikasi selesai untuk menghindari kerusakan pada aplikasi.


5
2017-12-17 11:47



Sebagaimana ditentukan dalam tutorial resmi oracle, itu menyatakan bahwa:

Jika klien dapat diharapkan pulih dari pengecualian,   menjadikannya pengecualian yang dicentang. Jika klien tidak dapat melakukan apa pun untuk pulih   dari pengecualian, menjadikannya sebagai pengecualian dicentang.

Jika saya memiliki Aplikasi yang berinteraksi dengan basis data menggunakan JDBC , Dan saya punya metode yang mengambil argumen sebagai int item dan double price. Itu price untuk item yang sesuai dibaca dari tabel database. Saya hanya mengalikan jumlah total item dibeli dengan price nilai dan kembalikan hasilnya. Meskipun saya selalu yakin di akhir saya (Aplikasi akhir) bahwa nilai bidang harga dalam tabel tidak pernah bisa negatif. Tapi bagaimana jika nilai harga keluar negatif? Ini menunjukkan bahwa ada masalah serius dengan sisi basis data. Mungkin salah harga masuk oleh operator. Ini adalah jenis masalah yang bagian lain dari aplikasi yang memanggil metode yang tidak dapat mengantisipasi dan tidak dapat pulih dari itu. Ini adalah sebuah BUG di database Anda. Jadi, dan IllegalArguementException() harus dibuang dalam kasus ini yang akan menyatakan itu the price can't be negative.
Saya berharap bahwa saya telah menyatakan poin saya dengan jelas ..


4
2018-03-04 19:06



Memperlakukan IllegalArgumentException sebagai prakondisi periksa, dan pertimbangkan prinsip desain: Metode publik harus tahu dan publik mendokumentasikan prakondisi sendiri.

Saya setuju contoh ini benar:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Jika EmailUtil tidak jelas, artinya ada beberapa alasan prekondisi tidak dapat dijelaskan kepada pengguna akhir, maka pengecualian yang dicentang benar. Versi kedua, dikoreksi untuk desain ini:

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

Jika EmailUtil transparan, misalnya mungkin itu adalah metode pribadi yang dimiliki oleh kelas yang dipertanyakan, IllegalArgumentException benar jika dan hanya jika prasyaratnya dapat dijelaskan dalam dokumentasi fungsi. Ini adalah versi yang benar juga:

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

Desain ini bisa jalan baik.

  • Jika prasyarat mahal untuk dijelaskan, atau jika kelas dimaksudkan untuk digunakan oleh klien yang tidak tahu apakah email mereka valid, maka gunakan ParseException. Metode tingkat atas di sini diberi nama scanEmail yang mengisyaratkan pengguna akhir bermaksud mengirim email tanpa email melalui jadi ini mungkin benar.
  • Jika prakondisi dapat dijelaskan dalam dokumentasi fungsi, dan kelas tidak bermaksud untuk input yang tidak valid dan karena itu kesalahan programmer diindikasikan, gunakan IllegalArgumentException. Meskipun tidak "memeriksa" langkah "periksa" ke Javadoc yang mendokumentasikan fungsi, yang diharapkan oleh klien untuk dipatuhi. IllegalArgumentException di mana klien tidak bisa mengatakan argumen mereka ilegal sebelumnya adalah salah.

Catatan tentang IllegalStateException: Ini berarti "keadaan internal objek ini (variabel instan instance) tidak dapat melakukan tindakan ini." Pengguna akhir tidak dapat melihat status pribadi sehingga berbicara secara longgar itu lebih diutamakan IllegalArgumentException dalam kasus di mana panggilan klien tidak memiliki cara untuk mengetahui status objek tidak konsisten. Saya tidak memiliki penjelasan yang baik ketika itu lebih disukai daripada mengecek pengecualian, meskipun hal-hal seperti menginisialisasi dua kali, atau kehilangan koneksi database yang belum pulih, adalah contoh.


0
2018-03-27 16:15