Apa kata kunci Java menegaskan lakukan, dan kapan harus digunakan?

601

Apa saja contoh kehidupan nyata untuk memahami peran kunci dari pernyataan?

Praveen
sumber
8
Dalam kehidupan nyata Anda hampir tidak pernah melihatnya. Dugaan: Jika Anda menggunakan penegasan, Anda harus memikirkan tiga kondisi: Tegaskan izin, tegaskan gagal, tegar dimatikan, bukan hanya dua. Dan menegaskan dimatikan secara default sehingga itu adalah keadaan yang paling mungkin, dan sulit untuk memastikan bahwa itu diaktifkan untuk kode Anda. Yang ditambahkan adalah bahwa menegaskan adalah pengoptimalan prematur yang akan digunakan terbatas. Seperti yang Anda lihat dalam jawaban @ Bjorn, bahkan sulit untuk membuat kasus penggunaan di mana Anda tidak ingin selalu gagal menegaskan.
Yishai
35
@Yishai: "Anda harus memikirkan ... menegaskan dimatikan" Jika Anda perlu melakukan itu, Anda salah melakukannya. "menegaskan adalah optimalisasi prematur untuk penggunaan terbatas" Ini cukup banyak. Inilah pendapat Sun tentang ini: " Menggunakan Pernyataan di Teknologi Java " dan ini juga baik untuk dibaca: " Manfaat pemrograman dengan pernyataan (alias pernyataan pernyataan) "
David Tonhofer
5
@ Davidvidoner, dalam kehidupan nyata Anda hampir tidak pernah melihat mereka. Ini bisa diverifikasi. Periksa sebanyak mungkin proyek sumber terbuka. Saya tidak mengatakan Anda tidak memvalidasi invarian. Itu bukan hal yang sama. Ambil jalan lain. Jika penegasan sangat penting, mengapa tidak aktif secara default?
Yishai
17
Referensi, FWIW: Hubungan antara pernyataan perangkat lunak dan kualitas kode : "Kami juga membandingkan kemanjuran pernyataan terhadap teknik penemuan bug populer seperti alat analisis statis kode sumber. Kami mengamati dari studi kasus kami bahwa dengan peningkatan kepadatan pernyataan dalam file ada penurunan yang signifikan secara statistik dalam kepadatan kesalahan. "
David Tonhofer
4
@ David Davidhofer, saya pikir cinta Anda untuk penegasan adalah untuk jenis pemrograman yang sangat spesifik yang kalian lakukan, di bidang saya yang bekerja dengan aplikasi web yang keluar dari program untuk alasan APAPUN adalah yang terbesar TIDAK TIDAK TIDAK - saya pribadi tidak pernah menggunakan penegasan selain pengujian unit / integ
nightograph

Jawaban:

426

Penegasan (dengan kata kunci penegasan ) ditambahkan di Java 1.4. Mereka digunakan untuk memverifikasi kebenaran invarian dalam kode. Mereka tidak boleh dipicu dalam kode produksi, dan merupakan indikasi bug atau penyalahgunaan jalur kode. Mereka dapat diaktifkan pada saat dijalankan dengan menggunakan -eaopsi pada javaperintah, tetapi tidak diaktifkan secara default.

Sebuah contoh:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}
Ophidian
sumber
71
Bahkan, Oracle memberi tahu Anda untuk tidak menggunakan assertuntuk memeriksa parameter metode publik ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ). Itu harus membuang Exceptionbukannya membunuh program.
SJuan76
10
Tetapi Anda masih belum menjelaskan mengapa mereka ada. Mengapa Anda tidak bisa melakukan if () memeriksa dan melempar pengecualian?
El Mac
7
@ ElMac - pernyataan untuk bagian dev / debug / test dari siklus - mereka bukan untuk produksi. Blok if berjalan di prod. Pernyataan sederhana tidak akan merusak bank, tetapi pernyataan mahal yang melakukan validasi data yang kompleks dapat menurunkan lingkungan produksi Anda, itulah sebabnya mereka dimatikan di sana.
hoodaticus
2
@hoodaticus maksud Anda semata-mata fakta bahwa saya dapat mengaktifkan / menonaktifkan semua pernyataan untuk kode prod adalah alasannya? Karena saya dapat melakukan validasi data yang kompleks dan menanganinya dengan pengecualian. Jika saya memiliki kode produksi, saya bisa mematikan pernyataan yang kompleks (dan mungkin mahal), karena harus bekerja dan sudah diuji? Secara teori mereka tidak boleh menurunkan program karena Anda akan memiliki masalah.
El Mac
8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…
Bakhshi
325

Mari kita asumsikan bahwa Anda seharusnya menulis sebuah program untuk mengendalikan pembangkit listrik tenaga nuklir. Sangat jelas bahwa kesalahan yang paling kecil sekalipun dapat memiliki hasil yang sangat besar, oleh karena itu kode Anda harus bebas bug (dengan asumsi bahwa JVM bebas bug untuk kepentingan argumen).

Java bukan bahasa yang dapat diverifikasi, yang berarti: Anda tidak dapat menghitung bahwa hasil operasi Anda akan sempurna. Alasan utama untuk ini adalah pointer: mereka dapat menunjuk di mana saja atau di mana saja, oleh karena itu mereka tidak dapat dihitung dengan nilai yang tepat ini, setidaknya tidak dalam rentang kode yang masuk akal. Mengingat masalah ini, tidak ada cara untuk membuktikan bahwa kode Anda benar secara keseluruhan. Tetapi yang dapat Anda lakukan adalah membuktikan bahwa Anda setidaknya menemukan setiap bug ketika itu terjadi.

Ide ini didasarkan pada paradigma Design-by-Contract (DbC): pertama-tama Anda mendefinisikan (dengan ketepatan matematis) apa yang seharusnya dilakukan oleh metode Anda, dan kemudian memverifikasi ini dengan mengujinya selama eksekusi aktual. Contoh:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

Meskipun ini cukup jelas untuk bekerja dengan baik, sebagian besar programmer tidak akan melihat bug tersembunyi di dalam yang satu ini (petunjuk: Ariane V jatuh karena bug yang sama). Sekarang DbC mendefinisikan bahwa Anda harus selalu memeriksa input dan output dari suatu fungsi untuk memverifikasi bahwa itu berfungsi dengan benar. Java dapat melakukan ini melalui pernyataan:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

Jika fungsi ini sekarang gagal, Anda akan melihatnya. Anda akan tahu bahwa ada masalah dalam kode Anda, Anda tahu di mana itu dan Anda tahu apa yang menyebabkannya (mirip dengan Pengecualian). Dan yang lebih penting: Anda berhenti mengeksekusi saat itu terjadi untuk mencegah kode lebih lanjut bekerja dengan nilai yang salah dan berpotensi menyebabkan kerusakan pada apa pun yang dikontrolnya.

Pengecualian Java adalah konsep yang serupa, tetapi mereka gagal memverifikasi semuanya. Jika Anda menginginkan lebih banyak cek (dengan biaya kecepatan eksekusi) Anda perlu menggunakan pernyataan. Melakukan hal itu akan mengasapi kode Anda, tetapi pada akhirnya Anda dapat mengirimkan produk pada waktu pengembangan yang sangat singkat (semakin cepat Anda memperbaiki bug, semakin rendah biayanya). Dan sebagai tambahan: jika ada bug di dalam kode Anda, Anda akan mendeteksinya. Tidak ada bug yang lolos dan menyebabkan masalah di kemudian hari.

Ini masih bukan jaminan untuk kode bebas bug, tetapi lebih dekat dengan itu, daripada program biasa.

Dua
sumber
29
Saya memilih contoh ini karena menyajikan bug tersembunyi dalam kode yang tampaknya bebas bug dengan sangat baik. Jika ini mirip dengan apa yang disajikan orang lain, maka mereka mungkin memiliki ide yang sama dalam pikiran. ;)
TwoThe
8
Anda memilih pernyataan karena gagal ketika pernyataan itu salah. Seandainya bisa memiliki perilaku. Memukul kasus pinggiran adalah pekerjaan Unit Testing. Menggunakan Desain dengan Kontrak menentukan kontrak dengan cukup baik tetapi seperti halnya dengan kontrak nyata, Anda perlu kontrol untuk memastikan mereka dihormati. Dengan penegasan, anjing penjaga dimasukkan yang akan memberi Anda saat kontrak tidak dihargai. Anggap saja sebagai pengacara yang mengomel berteriak "SALAH" setiap kali Anda melakukan sesuatu yang berada di luar atau melawan kontrak yang Anda tandatangani dan kemudian mengirim Anda pulang sehingga Anda tidak dapat terus bekerja dan melanggar kontrak lebih lanjut!
Eric
5
Diperlukan dalam kasus sederhana ini: tidak, tetapi DbC menentukan bahwa setiap hasil harus diperiksa. Bayangkan seseorang sekarang memodifikasi fungsi itu menjadi sesuatu yang jauh lebih kompleks, maka dia harus mengadaptasi post-check juga, dan kemudian tiba-tiba menjadi berguna.
TwoThe
4
Maaf untuk menghidupkan kembali ini, tetapi saya punya pertanyaan khusus. Apa perbedaan antara apa yang dilakukan @TwoThe dan alih-alih menggunakan penegasan hanya dengan melempar new IllegalArgumentExceptionpesan? Maksudku, selain harus menambahkan throwsdeklarasi metode dan kode untuk mengelola pengecualian itu di tempat lain. Mengapa assertharus membuang Exception baru? Atau mengapa bukan yang ifbukan assert? Tidak bisa mendapatkan ini :(
Blueriver
14
-1: Pernyataan untuk memeriksa overflow salah jika abisa negatif. Pernyataan kedua tidak berguna; untuk nilai int, selalu terjadi bahwa + b - b == a. Tes itu hanya bisa gagal jika komputer pada dasarnya rusak. Untuk bertahan dari kemungkinan itu, Anda perlu memeriksa konsistensi di beberapa CPU.
kevin cline
63

Pernyataan adalah alat fase pengembangan untuk menangkap bug dalam kode Anda. Mereka dirancang agar mudah dihapus, sehingga tidak akan ada dalam kode produksi. Jadi pernyataan bukan bagian dari "solusi" yang Anda berikan kepada pelanggan. Itu adalah pemeriksaan internal untuk memastikan bahwa asumsi yang Anda buat benar. Contoh paling umum adalah menguji nol. Banyak metode ditulis seperti ini:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Sangat sering dalam metode seperti ini, widget seharusnya tidak pernah menjadi nol. Jadi jika itu nol, ada bug di kode Anda di suatu tempat yang harus Anda lacak. Tetapi kode di atas tidak akan pernah memberi tahu Anda hal ini. Jadi dalam upaya yang bermaksud baik untuk menulis kode "aman", Anda juga menyembunyikan bug. Jauh lebih baik menulis kode seperti ini:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

Dengan cara ini, Anda pasti akan mengetahui bug ini lebih awal. (Ini juga berguna untuk menentukan dalam kontrak bahwa parameter ini tidak boleh nol.) Pastikan untuk mengaktifkan pernyataan ketika Anda menguji kode Anda selama pengembangan. (Dan membujuk kolega Anda untuk melakukan ini, juga seringkali sulit, yang menurut saya sangat menjengkelkan.)

Sekarang, beberapa kolega Anda akan keberatan dengan kode ini, dengan alasan bahwa Anda masih harus memasukkan cek nol untuk mencegah pengecualian dalam produksi. Dalam hal ini, pernyataan itu masih bermanfaat. Anda bisa menulis seperti ini:

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Dengan cara ini, kolega Anda akan senang bahwa cek nol ada untuk kode produksi, tetapi selama pengembangan, Anda tidak lagi menyembunyikan bug ketika widget nol.

Inilah contoh dunia nyata: Saya pernah menulis sebuah metode yang membandingkan dua nilai arbitrer untuk kesetaraan, di mana salah satu nilai bisa nol:

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

Kode ini mendelegasikan pekerjaan equals()metode dalam kasus di mana Nilai ini bukan nol. Tapi itu mengasumsikan equals()metode memenuhi kontrak equals()dengan benar dengan benar menangani parameter nol.

Seorang kolega keberatan dengan kode saya, memberi tahu saya bahwa banyak dari kelas kami memiliki equals()metode buggy yang tidak menguji nol, jadi saya harus memasukkan cek itu ke dalam metode ini. Dapat diperdebatkan apakah ini bijaksana, atau jika kita harus memaksakan kesalahan, sehingga kita dapat menemukannya dan memperbaikinya, tetapi saya menunda kolega saya dan memasukkan cek nol, yang saya tandai dengan komentar:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

Pemeriksaan tambahan di sini,, other != nullhanya diperlukan jika equals()metode gagal memeriksa nol seperti yang dipersyaratkan dalam kontraknya.

Daripada berdebat tanpa hasil dengan kolega saya tentang kebijaksanaan membiarkan kode buggy tetap di basis kode kami, saya hanya menempatkan dua pernyataan dalam kode. Pernyataan ini akan memberi tahu saya, selama fase pengembangan, jika salah satu kelas kami gagal diimplementasikan equals()dengan benar, jadi saya bisa memperbaikinya:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

Poin penting yang perlu diingat adalah ini:

  1. Pernyataan adalah alat fase pengembangan saja.

  2. Inti dari pernyataan ini adalah untuk memberi tahu Anda jika ada bug, tidak hanya di kode Anda, tetapi di basis kode Anda . (Pernyataan di sini sebenarnya akan menandai bug di kelas lain.)

  3. Bahkan jika kolega saya yakin bahwa kelas kami ditulis dengan benar, pernyataan di sini masih akan berguna. Kelas-kelas baru akan ditambahkan yang mungkin gagal untuk menguji null, dan metode ini dapat menandai bug tersebut untuk kita.

  4. Dalam pengembangan, Anda harus selalu mengaktifkan asersi, bahkan jika kode yang Anda tulis tidak menggunakan asersi. IDE saya diatur untuk selalu melakukan ini secara default untuk setiap executable baru.

  5. Pernyataan tidak mengubah perilaku kode dalam produksi, jadi kolega saya senang bahwa cek nol ada di sana, dan bahwa metode ini akan dieksekusi dengan baik bahkan jika equals()metode ini bermasalah. Saya senang karena saya akan menangkap equals()metode kereta apa pun dalam pengembangan.

Juga, Anda harus menguji kebijakan pernyataan Anda dengan memasukkan pernyataan sementara yang akan gagal, sehingga Anda dapat yakin bahwa Anda diberitahu, baik melalui file log atau jejak tumpukan di aliran output.

MiguelMunoz
sumber
Poin bagus tentang "menyembunyikan bug" dan bagaimana menegaskan mengekspos bug selama pengembangan!
nobar
Tidak satu pun dari cek ini yang lambat, jadi tidak ada alasan untuk mematikannya dalam produksi. Mereka harus dikonversi menjadi pernyataan logging, sehingga Anda dapat mendeteksi masalah yang tidak muncul di "fase pengembangan" Anda. (Sungguh, tidak ada yang namanya fase pengembangan. Pengembangan berakhir ketika Anda memutuskan untuk berhenti mempertahankan kode Anda sama sekali.)
Aleksandr Dubinsky
20

Banyak jawaban bagus yang menjelaskan apa assertkata kunci itu, tetapi hanya sedikit yang menjawab pertanyaan sebenarnya, "kapan assertkata kunci itu digunakan dalam kehidupan nyata?"

Jawabannya: hampir tidak pernah .

Pernyataan, sebagai konsep, sangat bagus. Kode yang baik memiliki banyak if (...) throw ...pernyataan (dan kerabat mereka suka Objects.requireNonNulldan Math.addExact). Namun, keputusan desain tertentu telah sangat membatasi kegunaan assert kata kunci itu sendiri.

Gagasan penggerak di balik assertkata kunci adalah pengoptimalan prematur, dan fitur utamanya adalah dapat dengan mudah mematikan semua cek. Bahkan, assertpemeriksaan dimatikan secara default.

Namun, sangat penting bahwa pemeriksaan invarian terus dilakukan dalam produksi. Ini karena cakupan pengujian yang sempurna tidak mungkin, dan semua kode produksi akan memiliki bug yang pernyataannya harus membantu untuk mendiagnosis dan mengurangi.

Oleh karena itu, penggunaan if (...) throw ...harus lebih disukai, seperti yang diperlukan untuk memeriksa nilai parameter metode publik dan untuk melempar IllegalArgumentException.

Kadang-kadang, orang mungkin tergoda untuk menulis cek invarian yang membutuhkan waktu lama untuk diproses (dan sering disebut cukup untuk membuatnya penting). Namun, pemeriksaan semacam itu akan memperlambat pengujian yang juga tidak diinginkan. Pemeriksaan yang menghabiskan waktu seperti itu biasanya ditulis sebagai unit test. Namun demikian, kadang-kadang mungkin masuk akal untuk menggunakan assertkarena alasan ini.

Jangan gunakan asserthanya karena lebih bersih dan lebih cantik dari if (...) throw ...(dan saya katakan itu dengan sangat sakit, karena saya suka bersih dan cantik). Jika Anda tidak dapat membantu diri Anda sendiri, dan dapat mengontrol bagaimana aplikasi Anda diluncurkan, maka jangan ragu untuk menggunakannya asserttetapi selalu aktifkan pernyataan dalam produksi. Harus diakui, inilah yang cenderung saya lakukan. Saya mendorong untuk anotasi lombok yang akan menyebabkan assertbertindak lebih seperti if (...) throw .... Pilih di sini.

(Kata-kata kasar: para pengembang JVM adalah sekelompok pembuat kode yang mengerikan, yang mengoptimalkan secara prematur. Itulah mengapa Anda mendengar tentang begitu banyak masalah keamanan dalam plugin Java dan JVM. Mereka menolak untuk memasukkan pemeriksaan dasar dan pernyataan dalam kode produksi, dan kami terus melanjutkan bayar harganya.)

Aleksandr Dubinsky
sumber
2
@ Agerglas Klausa catch-all adalah catch (Throwable t). Tidak ada alasan untuk tidak mencoba menjebak, mencatat, atau mencoba kembali / memulihkan dari OutOfMemoryError, AssertionError, dll.
Aleksandr Dubinsky
1
Saya telah menangkap dan pulih dari OutOfMemoryError.
MiguelMunoz
1
Saya tidak setuju. Banyak pernyataan saya digunakan untuk memastikan API saya dipanggil dengan benar. Sebagai contoh, saya mungkin menulis metode pribadi yang seharusnya hanya dipanggil ketika sebuah objek memegang kunci. Jika pengembang lain memanggil metode itu dari bagian kode yang tidak mengunci objek, pernyataan akan segera memberi tahu mereka bahwa mereka melakukan kesalahan. Ada banyak kesalahan seperti ini yang dapat, dengan pasti, terperangkap dalam fase pengembangan, dan pernyataan sangat berguna dalam kasus-kasus ini.
MiguelMunoz
2
@MiguelMunoz Dalam jawaban saya, saya mengatakan bahwa gagasan tentang asersi sangat bagus. Ini adalah implementasi dari assertkata kunci yang buruk. Saya akan mengedit jawaban saya untuk membuatnya lebih jelas bahwa saya merujuk pada kata kunci, bukan konsepnya.
Aleksandr Dubinsky
2
Saya suka fakta bahwa itu melempar AssertionError bukan Exception. Terlalu banyak pengembang yang belum mengetahui bahwa mereka seharusnya tidak menangkap Exception jika kode hanya dapat membuang sesuatu seperti IOException. Saya memiliki bug dalam kode saya yang sepenuhnya tertelan karena seseorang menangkap Exception. Pernyataan tidak terjebak dalam perangkap ini. Pengecualian untuk situasi yang Anda harapkan dilihat dalam kode produksi. Sedangkan untuk logging, Anda harus mencatat semua kesalahan Anda juga, meskipun kesalahan jarang terjadi. Misalnya, apakah Anda benar-benar ingin membiarkan OutOfMemoryError berlalu tanpa masuk?
MiguelMunoz
14

Inilah kasus penggunaan yang paling umum. Misalkan Anda mengaktifkan nilai enum:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

Selama Anda menangani setiap kasus, Anda baik-baik saja. Tetapi suatu hari, seseorang akan menambahkan ara ke enum Anda dan lupa untuk menambahkannya ke pernyataan pergantian Anda. Ini menghasilkan bug yang mungkin sulit ditangkap, karena efeknya tidak akan terasa sampai setelah Anda meninggalkan pernyataan pergantian. Tetapi jika Anda menulis pergantian Anda seperti ini, Anda dapat langsung menangkapnya:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}
MiguelMunoz
sumber
4
Itu sebabnya Anda harus mengaktifkan peringatan dan peringatan diperlakukan sebagai kesalahan. Kompilator setengah jalan mana pun mampu memberi tahu Anda, jika hanya Anda mengizinkannya memberi tahu Anda, bahwa Anda melewatkan cek enum, dan itu akan melakukannya pada waktu kompilasi, yang jauh lebih baik daripada (mungkin, satu hari) mencari tahu di waktu berjalan.
Mike Nakis
9
mengapa menggunakan pernyataan di sini alih-alih pengecualian dari beberapa jenis, misalnya, sebuah konsep argumen ilegal?
liltitus27
4
Ini akan memunculkan AssertionErrorpernyataan if jika diaktifkan ( -ea). Apa perilaku yang diinginkan dalam produksi? No-op sunyi dan potensi bencana nanti dalam eksekusi? Mungkin tidak. Saya akan menyarankan yang eksplisit throw new AssertionError("Missing enum value: " + fruit);.
aioobe
1
Ada argumen yang bagus untuk dibuat hanya dengan melemparkan AssertionError. Adapun perilaku yang tepat dalam produksi, inti dari pernyataan adalah untuk mencegah hal ini terjadi dalam produksi. Pernyataan adalah alat fase pengembangan untuk menangkap bug, yang dapat dengan mudah dihapus dari kode produksi. Dalam hal ini, tidak ada alasan untuk menghapusnya dari kode produksi. Tetapi dalam banyak kasus, tes integritas dapat memperlambat segalanya. Dengan meletakkan tes ini di dalam pernyataan, yang tidak digunakan dalam kode produksi, Anda bebas untuk menulis tes menyeluruh, tanpa khawatir mereka akan memperlambat kode produksi Anda.
MiguelMunoz
Ini sepertinya salah. IMHO Anda tidak harus menggunakan defaultsehingga kompiler dapat memperingatkan Anda tentang kasus yang hilang. Anda bisa returnalih-alih break(ini mungkin perlu mengekstraksi metode) dan kemudian menangani kasing yang hilang setelah sakelar. Dengan cara ini Anda mendapatkan peringatan dan peluang untuk melakukannya assert.
maaartinus
12

Penegasan digunakan untuk memeriksa prakondisi dan kondisi "tidak boleh gagal". Kode yang benar seharusnya tidak pernah gagal dalam penegasan; ketika mereka memicu, mereka harus menunjukkan bug (mudah-mudahan di tempat yang dekat dengan tempat masalah sebenarnya).

Contoh pernyataan mungkin untuk memeriksa bahwa kelompok metode tertentu dipanggil dalam urutan yang benar (misalnya, yang hasNext()disebut sebelumnya next()dalam suatu Iterator).

Donal Fellows
sumber
1
Anda tidak perlu memanggil hasNext () sebelum next ().
DJClayworth
6
@DJClayworth: Anda juga tidak perlu menghindari pemicu pernyataan. :-)
Donal Fellows
8

Apa kata kunci yang menegaskan di Jawa lakukan?

Mari kita lihat bytecode yang dikompilasi.

Kami akan menyimpulkan bahwa:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

menghasilkan bytecode yang hampir sama persis seperti:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

di mana Assert.class.desiredAssertionStatus()adalah trueketika-ea dilewatkan pada baris perintah, dan false jika tidak.

Kita gunakan System.currentTimeMillis() untuk memastikan bahwa itu tidak akan dioptimalkan ( assert true;lakukan).

Bidang sintetis dihasilkan sehingga Java hanya perlu memanggil Assert.class.desiredAssertionStatus()satu kali pada waktu pengambilan, dan kemudian menyimpan hasilnya di sana. Lihat juga: Apa arti "sintetis sintetik"?

Kami dapat memverifikasi itu dengan:

javac Assert.java
javap -c -constants -private -verbose Assert.class

Dengan Oracle JDK 1.8.0_45, bidang statis sintetik dihasilkan (lihat juga: Apa arti dari "sintetis sintetik"? ):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

bersama dengan penginisialisasi statis:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

dan metode utamanya adalah:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

Kami menyimpulkan bahwa:

  • tidak ada dukungan level bytecode untuk assert : itu adalah konsep bahasa Jawa
  • assertdapat ditiru dengan baik dengan properti sistem -Pcom.me.assert=trueuntuk menggantikan -eapada baris perintah, dan a throw new AssertionError().
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
2
Jadi catch (Throwable t)klausa juga bisa menangkap pelanggaran pernyataan? Bagi saya itu membatasi utilitas mereka hanya untuk kasus di mana tubuh pernyataan itu memakan waktu, yang jarang terjadi.
Evgeni Sergeev
1
Saya tidak yakin mengapa itu membatasi kegunaan pernyataan itu. Anda seharusnya tidak pernah menangkap Throwable kecuali dalam kasus yang sangat jarang. Jika Anda perlu menangkap Throwable tetapi ingin itu tidak menangkap pernyataan, Anda bisa menangkap yang AssertionErrorpertama dan menyusun kembali.
MiguelMunoz
7

Contoh dunia nyata, dari kelas Stack (dari Assertion in Java Artikel )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}
Björn
sumber
80
Ini akan disukai di C: Penegasan adalah sesuatu yang BENAR-BENAR TIDAK AKAN terjadi - memunculkan tumpukan kosong harus melempar NoElementsException atau sesuatu seperti itu. Lihat balasan Donal.
Konerak
4
Saya setuju. Meskipun ini diambil dari tutorial resmi, ini adalah contoh yang buruk.
DJClayworth
7
Mungkin ada kebocoran memori di sana. Anda harus mengatur stack [num] = null; agar GC melakukan tugasnya dengan benar.
H.Rabiee
3
Saya pikir dalam metode pribadi, akan benar untuk menggunakan pernyataan, karena akan aneh memiliki pengecualian untuk kerusakan kelas atau metode. Dalam metode publik, memanggilnya dari suatu tempat di luar, Anda tidak dapat benar-benar tahu bagaimana kode lain menggunakannya. Apakah ini benar-benar memeriksa isEmpty () atau tidak? Kamu tidak tahu.
Vlasec
7

Pernyataan memungkinkan untuk mendeteksi cacat dalam kode. Anda dapat mengaktifkan pernyataan untuk pengujian dan debugging sambil membiarkannya ketika program Anda sedang dalam produksi.

Mengapa menegaskan sesuatu ketika Anda tahu itu benar? Hanya benar ketika semuanya bekerja dengan baik. Jika program memiliki cacat, itu mungkin sebenarnya tidak benar. Mendeteksi ini sebelumnya dalam proses membuat Anda tahu ada sesuatu yang salah.

Sebuah assertpernyataan berisi pernyataan ini bersama dengan opsional Stringpesan.

Sintaks untuk pernyataan tegas memiliki dua bentuk:

assert boolean_expression;
assert boolean_expression: error_message;

Berikut adalah beberapa aturan dasar yang mengatur di mana pernyataan harus digunakan dan di mana mereka seharusnya tidak digunakan. Pernyataan harus digunakan untuk:

  1. Memvalidasi parameter input dari metode pribadi. BUKAN untuk metode publik. publicmetode harus membuang pengecualian reguler ketika melewati parameter buruk.

  2. Di mana saja dalam program untuk memastikan validitas fakta yang hampir pasti benar.

Misalnya, jika Anda yakin hanya 1 atau 2, Anda dapat menggunakan pernyataan seperti ini:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Memvalidasi kondisi pos di akhir metode apa pun. Ini berarti, setelah menjalankan logika bisnis, Anda dapat menggunakan pernyataan untuk memastikan bahwa keadaan internal variabel atau hasil Anda konsisten dengan apa yang Anda harapkan. Misalnya, metode yang membuka soket atau file dapat menggunakan pernyataan di akhir untuk memastikan bahwa soket atau file memang dibuka.

Pernyataan tidak boleh digunakan untuk:

  1. Memvalidasi parameter input dari metode publik. Karena pernyataan mungkin tidak selalu dieksekusi, mekanisme pengecualian reguler harus digunakan.

  2. Memvalidasi kendala pada sesuatu yang diinput oleh pengguna. Sama seperti di atas.

  3. Seharusnya tidak digunakan untuk efek samping.

Sebagai contoh, ini bukan penggunaan yang tepat karena di sini pernyataan digunakan untuk efek samping dari pemanggilan doSomething()metode.

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

Satu-satunya kasus di mana ini dapat dibenarkan adalah ketika Anda mencoba mencari tahu apakah pernyataan diaktifkan atau tidak dalam kode Anda:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}
solomkinmv
sumber
5

Selain semua jawaban hebat yang diberikan di sini, panduan pemrograman Java SE 7 resmi memiliki manual yang cukup ringkas tentang penggunaan assert; dengan beberapa contoh langsung ketika itu ide yang bagus (dan, yang penting, buruk) untuk menggunakan pernyataan, dan bagaimana hal itu berbeda dari melemparkan pengecualian.

Tautan

Ivan Bartsov
sumber
1
Saya setuju. Artikel ini memiliki banyak contoh bagus. Saya terutama menyukai yang memastikan metode hanya dipanggil ketika objek memegang kunci.
MiguelMunoz
4

Ketegasan sangat bermanfaat saat berkembang. Anda menggunakannya ketika sesuatu tidak bisa terjadi jika kode Anda bekerja dengan benar. Ini mudah digunakan, dan dapat tetap berada dalam kode untuk selamanya, karena akan dimatikan dalam kehidupan nyata.

Jika ada kemungkinan kondisi tersebut dapat terjadi dalam kehidupan nyata, maka Anda harus menanganinya.

Saya menyukainya, tetapi tidak tahu cara menyalakannya di Eclipse / Android / ADT. Tampaknya tidak aktif bahkan ketika debugging. (Ada utas tentang ini, tetapi merujuk pada 'Java vm', yang tidak muncul dalam Konfigurasi Jalankan ADT).

John White
sumber
1
Untuk mengaktifkan pernyataan dalam eclipse IDE, silakan ikuti tutoringcenter.cs.usfca.edu/resources/…
Ayaz Pasha
Saya tidak berpikir ada cara untuk mengaktifkan pernyataan di Android. Ini sangat mengecewakan.
MiguelMunoz
3

Berikut adalah pernyataan yang saya tulis di server untuk proyek Hibernate / SQL. Entitas kacang memiliki dua sifat boolean yang efektif, yang disebut isActive dan isDefault. Masing-masing dapat memiliki nilai "Y" atau "N" atau null, yang diperlakukan sebagai "N". Kami ingin memastikan klien browser dibatasi pada tiga nilai ini. Jadi, di setter saya untuk dua properti ini, saya menambahkan pernyataan ini:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

Perhatikan yang berikut ini.

  1. Penegasan ini hanya untuk fase pengembangan. Jika klien mengirimkan nilai buruk, kami akan menangkapnya lebih awal dan memperbaikinya, jauh sebelum kami mencapai produksi. Pernyataan untuk cacat yang bisa Anda tangkap lebih awal.

  2. Penegasan ini lambat dan tidak efisien. Tidak apa-apa. Pernyataan bebas untuk lambat. Kami tidak peduli karena mereka hanya alat pengembangan. Ini tidak akan memperlambat kode produksi karena pernyataan akan dinonaktifkan. (Ada beberapa ketidaksepakatan tentang hal ini, yang akan saya bahas nanti.) Ini mengarah ke poin saya berikutnya.

  3. Pernyataan ini tidak memiliki efek samping. Saya bisa menguji nilai saya terhadap Set final statis yang tidak dapat dimodifikasi, tetapi set itu akan tetap ada dalam produksi, di mana ia tidak akan pernah digunakan.

  4. Pernyataan ini ada untuk memverifikasi operasi klien yang tepat. Jadi pada saat kami mencapai produksi, kami akan yakin bahwa klien beroperasi dengan benar, sehingga kami dapat dengan aman mematikan pernyataan tersebut.

  5. Beberapa orang bertanya ini: Jika pernyataan tidak diperlukan dalam produksi, mengapa tidak mengeluarkannya begitu selesai? Karena Anda masih akan membutuhkannya saat Anda mulai mengerjakan versi berikutnya.

Beberapa orang berpendapat bahwa Anda seharusnya tidak pernah menggunakan pernyataan, karena Anda tidak pernah bisa yakin bahwa semua bug telah hilang, jadi Anda perlu menjaga mereka di sekitar bahkan dalam produksi. Jadi tidak ada gunanya menggunakan pernyataan tegas, karena satu-satunya keuntungan untuk menegaskan adalah bahwa Anda dapat mematikannya. Karenanya, menurut pemikiran ini, Anda seharusnya (hampir) tidak pernah menggunakan penegasan. Saya tidak setuju. Memang benar bahwa jika sebuah tes termasuk dalam produksi, Anda tidak boleh menggunakan pernyataan. Tetapi tes ini tidak termasuk dalam produksi. Yang ini untuk menangkap bug yang kemungkinan besar tidak akan pernah mencapai produksi, sehingga dapat dimatikan dengan aman ketika Anda selesai.

BTW, saya bisa menulisnya seperti ini:

assert value == null || value.equals("Y") || value.equals("N") : value;

Ini bagus untuk hanya tiga nilai, tetapi jika jumlah nilai yang mungkin semakin besar, versi HashSet menjadi lebih nyaman. Saya memilih versi HashSet untuk menjelaskan efisiensi.

MiguelMunoz
sumber
Saya sangat meragukan bahwa menggunakan yang sekecil itu HashSetmembawa keunggulan kecepatan apa pun di atas ArrayList. Selain itu, kreasi kumpulan dan daftar mendominasi waktu pencarian. Mereka akan baik-baik saja ketika menggunakan konstanta. Itu semua berkata, +1.
maaartinus
Semua benar. Saya melakukannya dengan cara yang tidak efisien ini untuk mengilustrasikan poin saya bahwa pernyataan bebas menjadi lambat. Yang ini bisa dibuat lebih efisien, tetapi ada orang lain yang tidak bisa. Dalam sebuah buku bagus yang disebut "Writing Solid Code," Steve Maguire menceritakan tentang pernyataan dalam Microsoft Excel untuk menguji kode pembaruan-tambahan baru yang melewatkan sel yang seharusnya tidak berubah. Setiap kali pengguna melakukan perubahan, pernyataan itu akan menghitung ulang seluruh spreadsheet untuk memastikan hasilnya cocok dengan fitur pembaruan tambahan. Ini benar-benar memperlambat versi debug, tetapi mereka menangkap semua bug mereka lebih awal.
MiguelMunoz
Sepenuhnya disetujui. Pernyataan adalah semacam tes - mereka kurang fleksibel daripada tes biasa, tetapi mereka dapat mencakup metode pribadi dan mereka jauh lebih murah untuk menulis. Saya akan mencoba menggunakannya lebih banyak lagi.
maaartinus
2

Pernyataan pada dasarnya digunakan untuk men-debug aplikasi atau digunakan sebagai pengganti penanganan pengecualian untuk beberapa aplikasi untuk memeriksa validitas aplikasi.

Penegasan bekerja pada saat dijalankan. Sebuah contoh sederhana, yang dapat menjelaskan keseluruhan konsep dengan sangat sederhana, ada di sini - Apa kata kunci tegas lakukan di Jawa?(WikiAnswers).

SBTec
sumber
2

Pernyataan dinonaktifkan secara default. Untuk mengaktifkannya, kita harus menjalankan program dengan -eaopsi (granularity dapat bervariasi). Sebagai contoh java -ea AssertionsDemo,.

Ada dua format untuk menggunakan pernyataan:

  1. Sederhana: mis. assert 1==2; // This will raise an AssertionError.
  2. Lebih baik: assert 1==2: "no way.. 1 is not equal to 2"; Ini akan memunculkan AssertionError dengan pesan yang diberikan juga ditampilkan dan karenanya lebih baik. Meskipun sintaks sebenarnya adalah di assert expr1:expr2mana expr2 dapat berupa ekspresi apa pun yang mengembalikan nilai, saya telah menggunakannya lebih sering hanya untuk mencetak pesan.
Chandan Purohit
sumber
1

Untuk rekap (dan ini berlaku untuk banyak bahasa bukan hanya Jawa):

"menegaskan" terutama digunakan sebagai bantuan debugging oleh pengembang perangkat lunak selama proses debugging. Pesan tegas seharusnya tidak pernah muncul. Banyak bahasa menyediakan opsi waktu kompilasi yang akan menyebabkan semua "menegaskan" diabaikan, untuk digunakan dalam menghasilkan kode "produksi".

"Pengecualian" adalah cara praktis untuk menangani semua jenis kondisi kesalahan, apakah itu merupakan kesalahan logika atau tidak, karena, jika Anda mengalami kondisi kesalahan sehingga Anda tidak dapat melanjutkan, Anda bisa "membuangnya ke udara," "Dari manapun Anda berada, mengharapkan orang lain di luar sana siap untuk" menangkap "mereka. Kontrol ditransfer dalam satu langkah, langsung dari kode yang melemparkan pengecualian, langsung ke sarung tangan penangkap. (Dan penangkap dapat melihat jejak balik lengkap dari panggilan yang telah terjadi.)

Lebih jauh, penelepon subrutin itu tidak perlu memeriksa untuk melihat apakah subrutin itu berhasil: "jika kita di sini sekarang, itu pasti berhasil, karena kalau tidak maka akan ada pengecualian dan kita tidak akan ada di sini sekarang!"Strategi sederhana ini membuat desain kode dan debugging menjadi jauh lebih mudah.

Pengecualian memungkinkan kondisi kesalahan fatal seperti apa adanya: "pengecualian terhadap aturan." Dan, bagi mereka untuk ditangani oleh jalur kode yang juga "pengecualian dari aturan ... " terbang bola! "

Mike Robinson
sumber
1

Penegasan adalah cek yang dapat dimatikan. Mereka jarang digunakan. Mengapa?

  • Mereka tidak boleh digunakan untuk memeriksa argumen metode publik karena Anda tidak memiliki kontrol atasnya.
  • Mereka tidak boleh digunakan untuk cek sederhana seperti result != nullkarena cek seperti itu sangat cepat dan hampir tidak ada yang bisa disimpan.

Jadi, apa yang tersisa? Pemeriksaan mahal untuk kondisi benar-benar diharapkan menjadi kenyataan. Contoh yang baik adalah invarian dari struktur data seperti RB-tree. Sebenarnya, di ConcurrentHashMapJDK8, ada beberapa pernyataan bermakna seperti untuk TreeNodes.

  • Anda benar-benar tidak ingin menyalakannya dalam produksi karena mereka dapat dengan mudah mendominasi waktu berjalan.
  • Anda mungkin ingin mengaktifkan atau menonaktifkannya selama pengujian.
  • Anda pasti ingin mengaktifkannya ketika bekerja pada kode.

Terkadang, ceknya tidak terlalu mahal, tetapi pada saat yang sama, Anda cukup yakin, itu akan berlalu. Dalam kode saya, ada misalnya,

assert Sets.newHashSet(userIds).size() == userIds.size();

di mana saya cukup yakin bahwa daftar yang baru saja saya buat memiliki elemen unik, tetapi saya ingin mendokumentasikan dan mengeceknya.

maaartinus
sumber
0

Pada dasarnya, "menegaskan benar" akan berlalu dan "menyatakan salah" akan gagal. Mari kita lihat bagaimana ini akan bekerja:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}
Peter Mortensen
sumber
-8

assertadalah kata kunci. Itu diperkenalkan di JDK 1.4. Ada dua jenis asserts

  1. assertPernyataan yang sangat sederhana
  2. assertPernyataan sederhana .

Secara default semua assertpernyataan tidak akan dieksekusi. Jika sebuah assertpernyataan menerima false, maka secara otomatis akan memunculkan kesalahan pernyataan.

pavani
sumber
1
Itu tidak memberikan contoh kehidupan nyata, yang merupakan tujuan dari pertanyaan
rubenafo
1
Apakah Anda baru saja menyalin tempel dari: amazon.com/Programmer-Study-1Z0-803-1Z0-804-Certification/dp/… ?
Koray Tugay