Apakah lebih baik menggunakan assert atau IllegalArgumentException untuk parameter metode yang diperlukan?

87

Di Jawa, mana yang lebih direkomendasikan, dan mengapa? Kedua jenis akan melempar pengecualian, sehingga dalam hal penanganannya adalah sama. assertsedikit lebih pendek, tapi saya tidak yakin seberapa penting itu.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

vs.

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}
Daenyth
sumber
Saya lebih suka yang sederhana obj.hashCode();-)
Marco

Jawaban:

118

WASPADALAH!

Pernyataan dihapus pada saat runtime kecuali Anda secara eksplisit menentukan untuk "mengaktifkan pernyataan" saat mengkompilasi kode Anda. Penegasan Java tidak boleh digunakan pada kode produksi dan harus dibatasi pada metode pribadi (lihat Pengecualian vs Penegasan ), karena metode pribadi diharapkan untuk diketahui dan digunakan hanya oleh pengembang. Juga assertakan melempar AssertionError yang Errortidak meluas Exception, dan yang biasanya menunjukkan Anda memiliki kesalahan yang sangat tidak normal (seperti "OutOfMemoryError" yang sulit untuk dipulihkan, bukan?) Anda tidak diharapkan dapat mengobati.

Hapus bendera "aktifkan pernyataan", dan periksa dengan debugger dan Anda akan melihat bahwa Anda tidak akan menginjak panggilan pemanggilan IllegalArgumentException ... karena kode ini belum dikompilasi (sekali lagi, ketika "ea" dihapus)

Lebih baik menggunakan konstruksi kedua untuk metode publik / dilindungi, dan jika Anda menginginkan sesuatu yang dilakukan dalam satu baris kode, setidaknya ada satu cara yang saya tahu. Saya pribadi menggunakan Spring Framework 's Assertkelas yang memiliki beberapa metode untuk memeriksa argumen dan melemparkan 'IllegalArgumentException' pada kegagalan. Pada dasarnya, yang Anda lakukan adalah:

Assert.notNull(obj, "object was null");

... Yang sebenarnya akan mengeksekusi kode yang persis sama dengan yang Anda tulis pada contoh kedua. Ada beberapa metode lain yang bermanfaat seperti hasText, hasLengthdi sana.

Saya tidak suka menulis lebih banyak kode daripada yang diperlukan, jadi saya senang ketika saya mengurangi jumlah baris yang ditulis sebanyak 2 (2 baris> 1 baris) :-)

Jalayn
sumber
Ah, saya lupa tentang pernyataan yang dihapus! Jawaban yang bagus Saya akan menunggu sedikit untuk melihat apakah ada sesuatu yang masuk, kemudian menerimanya :)
Daenyth
2
Perhatikan bahwa tidak ada bendera yang menghapus pernyataan pada waktu kompilasi (meskipun mereka dapat dihilangkan melalui kompliasi bersyarat ). Pernyataan dinonaktifkan pada saat runtime secara default (saya pikir JVM memperlakukannya sebagai NOOP), tetapi dapat diaktifkan melalui java -eadan secara terprogram. @ Jalayn Saya pikir memiliki pernyataan dalam kode produksi sangat valid, karena mereka berguna untuk debugging di lapangan
Justin Muller
@Jalayn, -1. Kompiler tidak menghapus kode pernyataan. Meskipun mereka tidak akan dijalankan kecuali jika Anda melakukan cmd java -ea.
Pacerier
5
tidak perlu untuk kerangka Spring ketika Anda dapat menggunakanObjects.requreNonNull
cambunctious
46

Anda perlu menggunakan pengecualian. Menggunakan pernyataan akan menjadi penyalahgunaan fitur.

Pengecualian yang tidak dicentang dirancang untuk mendeteksi kesalahan pemrograman pengguna perpustakaan Anda, sementara pernyataan dirancang untuk mendeteksi kesalahan dalam logika Anda sendiri . Ini adalah masalah terpisah yang tidak boleh dicampur.

Misalnya, sebuah pernyataan

assert myConnection.isConnected();

berarti "Saya tahu bahwa setiap jalur kode yang mengarah ke pernyataan ini memastikan bahwa myConnectionterhubung; jika kode di atas gagal mendapatkan koneksi yang valid, itu harusnya melemparkan pengecualian atau kembali sebelum mencapai titik ini."

Di sisi lain, cek

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

berarti bahwa "Memanggil perpustakaan saya tanpa membuat sambungan adalah kesalahan pemrograman".

dasblinkenlight
sumber
1
Informasi ini sangat membantu, tetapi saya menerima Jalayn karena ini menunjukkan bagaimana saya berpotensi memperkenalkan bug dengan metode tegas.
Daenyth
4
poin yang sangat bagus "Pengecualian yang tidak dicentang dirancang untuk mendeteksi kesalahan pemrograman dari pengguna perpustakaan Anda, sementara pernyataan dirancang untuk mendeteksi kesalahan dalam logika Anda sendiri. Ini adalah masalah terpisah yang tidak boleh dicampur."
Asim Ghaffar
Benar, tetapi ini mengasumsikan OP sedang menulis perpustakaan. Jika kode mereka hanya untuk penggunaan internal, pernyataan dapat diterima.
user949300
11

Jika Anda menulis fungsi yang tidak memungkinkan nullsebagai nilai parameter yang valid, Anda harus menambahkan @Nonnullanotasi ke tanda tangan dan gunakan Objects.requireNonNulluntuk memeriksa apakah argumennya adalah nulldan melemparkan a NullPointerExceptionjika itu. The @Nonnullpenjelasan adalah untuk dokumentasi dan akan memberikan peringatan membantu pada waktu kompilasi dalam beberapa kasus. Itu tidak mencegah nulldari diteruskan saat runtime.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Memperbarui: The @Nonnullpenjelasan bukan bagian dari Java perpustakaan standar. Sebagai gantinya, ada banyak standar yang bersaing dari perpustakaan pihak ketiga (lihat Anotasi @NotNull Java mana yang harus saya gunakan? ). Itu tidak berarti itu ide yang buruk untuk menggunakannya, hanya saja itu bukan standar.

cambunctious
sumber
Terima kasih telah menunjukkan Objects.requireNonNull(), yang baru bagi saya. Apakah Anda tahu jika ada metode "requireTrue ()" atau "requireEqual ()" yang serupa di mana saja? Tidak ada yang menentang Spring's Assert tetapi tidak semua orang menggunakannya.
user949300
@ user949300 Objects.requireNonNull()untuk validasi argumen. Jika suatu argumen diperlukan untuk menjadi trueatau sama dengan sesuatu, maka argumen itu tidak ada gunanya. Untuk kasus kesalahan selain argumen ilegal, Anda harus throwseorang Exceptionyang lebih akurat menggambarkan kesalahan. Ada juga JUnit Asserttapi itu untuk tes.
cambunctious
Saya berpikir lebih seperti, katakanlah untuk fungsi akar kuadrat Objects.requireTrue(x >= 0.0);,, atau untuk beberapa hash,Objects.requireEquals(hash.length == 256)
user949300
@Nonnull mana yang harus saya gunakan? javax.validation.constraints.NotNull?
Aguid
Saya akan menggunakan anotasi Eclipse JDT , karena dibuat oleh orang pintar :). Dokumentasi: help.eclipse.org/neon/… - Anda juga dapat mengonfigurasi IntelliJ untuk menggunakannya.
koppor
2

Saya selalu lebih suka melempar IllegalArgumentException daripada pernyataan.

Penegasan digunakan sebagian besar di JUnit atau alat pengujian lainnya, untuk memeriksa / menegaskan hasil pengujian. Jadi mungkin memberi kesan palsu kepada pengembang lain bahwa metode Anda adalah metode pengujian.

Juga masuk akal untuk melempar IllegalArgumentException ketika suatu metode telah melewati argumen ilegal atau tidak pantas . Ini lebih konsisten dengan konvensi Penanganan Pengecualian yang diikuti oleh pengembang Java.

rai.skumar
sumber
5
Pernyataan sekitar 40 tahun sebelum JUnit - tanyakan programmer C tentang makro ASSERT.
JBRWilkinson
3
pertanyaan ini bukan tentang c; ini tentang Jawa. Jadi saya jawab dalam konteks Jawa.
rai.skumar
Jangan bingung assert (kata kunci yang disediakan) vs Assert (kelas JUnit) mereka berdua digunakan untuk melakukan pemeriksaan pada variabel tetapi di luar itu mereka adalah dua hal yang sangat berbeda dan berperilaku sangat berbeda.
Newtopian
1

IMO yang kedua sedikit lebih baik karena membawa lebih banyak informasi dan dapat diperpanjang (misalnya dengan memperluas kelas pengecualian) menjadi lebih informatif, juga tidak menggunakan perbandingan negatif yang lebih mudah dipahami.

0lukasz0
sumber
1

Saya tidak banyak menggunakan aserts, tetapi pendekatan umum dengan Lombock @NonNull: https://projectlombok.org/features/NonNull

Implementasi Lombok: impor lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Versi Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok adalah perpustakaan yang sangat bagus yang saya gunakan di mana-mana

kensai
sumber