Cara menggunakan penegasan di Java

Menulis program yang berfungsi dengan betul pada waktu berjalan boleh menjadi cabaran. Ini kerana andaian kita tentang bagaimana kod kita akan berkelakuan ketika dilaksanakan sering salah. Menggunakan ciri penegasan Java adalah salah satu cara untuk mengesahkan bahawa logik pengaturcaraan anda betul.

Tutorial ini memperkenalkan penegasan Java. Anda akan terlebih dahulu mengetahui apa itu penegasan dan cara menentukan dan menggunakannya dalam kod anda. Seterusnya, anda akan mengetahui cara menggunakan penegasan untuk menguatkuasakan prasyarat dan syarat. Akhirnya, anda akan membandingkan pernyataan dengan pengecualian, dan mengetahui mengapa anda memerlukan kedua-duanya dalam kod anda.

muat turun Dapatkan kod Muat turun kod sumber untuk contoh dalam tutorial ini. Dicipta oleh Jeff Friesen untuk JavaWorld.

Apakah penegasan Java?

Sebelum JDK 1.4, pembangun sering menggunakan komen untuk mendokumentasikan andaian mengenai kebenaran program. Komen tidak berguna sebagai mekanisme pengujian dan penyahpepijatan andaian. Penyusun mengabaikan komen, jadi tidak ada cara untuk menggunakannya untuk pengesanan pepijat. Pembangun juga sering tidak mengemas kini komen semasa menukar kod.  

Dalam JDK 1.4, penegasan diperkenalkan sebagai mekanisme baru untuk menguji dan menyahpepetikan andaian mengenai kod kami. Pada hakikatnya, penegasan  adalah entiti yang dapat disusun yang dijalankan pada waktu runtime, dengan andaian anda telah mengaktifkannya untuk ujian program. Anda boleh memprogramkan penegasan untuk memberitahu anda tentang bug di mana bug itu berlaku, mengurangkan banyak masa yang anda habiskan untuk menyelesaikan masalah program yang gagal.

Tegasan digunakan untuk mengkodifikasi keperluan yang menjadikan program betul atau tidak dengan menguji keadaan (ungkapan Boolean) untuk nilai benar, dan memberitahu pembangun apabila keadaan tersebut salah. Menggunakan penegasan dapat meningkatkan keyakinan anda terhadap kebenaran kod anda.

Cara menulis penegasan di Jawa

Penegasan dilaksanakan melalui assertpenyataan dan java.lang.AssertionErrorkelas. Pernyataan ini bermula dengan kata kunci assertdan dilanjutkan dengan ungkapan Boolean. Ia dinyatakan secara sintaksis seperti berikut:

menegaskan BooleanExpr ;

Sekiranya BooleanExprdinilai benar, tidak ada yang berlaku dan pelaksanaannya berterusan. Jika ungkapan dinilai menjadi salah, bagaimanapun, AssertionErrordisusun dan dilemparkan, seperti yang ditunjukkan dalam Penyenaraian 1.

Penyenaraian 1:AssertDemo.java (versi 1)

kelas awam AssertDemo {public static void main (String [] args) {int x = -1; tegaskan x> = 0; }}

Tegasan dalam Penyenaraian 1 menunjukkan kepercayaan pemaju bahawa pemboleh ubah xmengandungi nilai yang lebih besar daripada atau sama dengan 0. Walau bagaimanapun, ini jelas tidak berlaku; pelaksanaan assertpenyataan itu menghasilkan lontaran AssertionError.

Kumpulkan Penyenaraian 1 ( javac AssertDemo.java) dan jalankan dengan penegasan yang diaktifkan ( java -ea AssertDemo). Anda harus memerhatikan output berikut:

Pengecualian dalam utas "utama" java.lang.AssertionError di AssertDemo.main (AssertDemo.java:6)

Mesej ini agak samar kerana tidak mengenal pasti apa yang menyebabkannya AssertionErrordilemparkan. Sekiranya anda mahukan mesej yang lebih bermaklumat, gunakan assertpernyataan yang dinyatakan di bawah:

ceritakan BooleanExpr : expr ;

Di sini, exprterdapat sebarang ungkapan (termasuk pemanggilan kaedah) yang dapat mengembalikan nilai - anda tidak boleh menggunakan kaedah dengan voidjenis pengembalian. Ungkapan yang berguna adalah literal string yang menjelaskan alasan kegagalan, seperti yang ditunjukkan dalam Penyenaraian 2.

Penyenaraian 2:AssertDemo.java (versi 2)

kelas awam AssertDemo {public static void main (String [] args) {int x = -1; tegaskan x> = 0: "x <0"; }}

Susun Penyenaraian 2 ( javac AssertDemo.java) dan jalankan dengan penegasan yang diaktifkan ( java -ea AssertDemo). Kali ini, anda harus memerhatikan output yang sedikit diperluas berikut, yang merangkumi alasan untuk dilemparkan AssertionError:

Pengecualian dalam utas "utama" java.lang.AssertionError: x <0 di AssertDemo.main (AssertDemo.java:6)

Untuk kedua-dua contoh, berjalan AssertDemotanpa pilihan -ea(aktifkan penegasan) tidak menghasilkan output. Apabila penegasan tidak diaktifkan, pernyataan tersebut tidak dilaksanakan, walaupun masih terdapat dalam fail kelas.

Pra-syarat dan pasca syarat

Penegasan menguji andaian program dengan mengesahkan bahawa pelbagai prasyarat dan keadaannya tidak dilanggar, memberi amaran kepada pembangun apabila pelanggaran berlaku:

  • A pra-syarat adalah suatu keadaan yang mesti menilai kepada benar sebelum pelaksanaan beberapa urutan kod. Prasyarat memastikan bahawa pemanggil menyimpan kontrak mereka dengan pemanggil.
  • A postcondition adalah suatu keadaan yang mesti menilai kepada benar selepas pelaksanaan beberapa urutan kod. Pasca syarat memastikan bahawa pemanggil menyimpan kontrak mereka dengan pemanggil.

Prasyarat

Anda boleh melaksanakan prasyarat pada pembangun dan kaedah awam dengan membuat pemeriksaan eksplisit dan membuang pengecualian apabila perlu. Untuk kaedah pembantu peribadi, anda boleh melaksanakan prasyarat dengan menentukan pernyataan. Pertimbangkan Penyenaraian 3.

Penyenaraian 3:AssertDemo.java (versi 3)

import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; kelas PNG {/ ** * Buat instance PNG, baca fail PNG yang ditentukan, dan nyahkodkannya * menjadi struktur yang sesuai. * * @param filespec path dan nama fail PNG untuk dibaca * * @throws NullPointerException bilakah filespec*null* / PNG (String filespec) melemparkan IOException {// Menguatkuasakan prasyarat dalam pembina dan kaedah bukan swasta. jika (filespec == null) membuang NullPointerException baru ("filespec is null"); cuba (FileInputStream fis = FileInputStream baru (filespec)) {readHeader (fis); }} readHeader kekosongan peribadi (InputStream is) melemparkan IOException {// Sahkan bahawa prasyarat dipenuhi dalam kaedah // pembantu peribadi. menegaskan adalah! = null: "null diteruskan ke adalah"; }} kelas awam AssertDemo {public static void main (String [] args) membuang IOException {PNG png = PNG baru ((args.length == 0)? null: args [0]); }}

The PNG class in Listing 3 is the minimal beginning of a library for reading and decoding PNG (portable network graphics) image files. The constructor explicitly compares filespec with null, throwing NullPointerException when this parameter contains null. The point is to enforce the precondition that filespec not contain null.

It’s not appropriate to specify assert filespec != null; because the precondition mentioned in the constructor’s Javadoc would not (technically) be honored when assertions were disabled. (In fact, it would be honored because FileInputStream() would throw NullPointerException, but you shouldn’t depend on undocumented behavior.)

Namun, assertsesuai dalam konteks readHeader()kaedah pembantu peribadi , yang akhirnya akan selesai untuk membaca dan menyahkod header 8-byte fail PNG. Prasyarat yang isselalu dilalui nilai bukan nol akan selalu berlaku.

Pasca syarat

Postkondisi biasanya ditentukan melalui penegasan, tanpa mengira sama ada kaedah (atau pembina) adalah umum. Pertimbangkan Penyenaraian 4.

Penyenaraian 4:AssertDemo.java (versi 4)

public class AssertDemo { public static void main(String[] args) { int[] array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 }; sort(array); for (int element: array) System.out.printf("%d ", element); System.out.println(); } private static boolean isSorted(int[] x) { for (int i = 0; i  x[i + 1]) return false; return true; } private static void sort(int[] x) { int j, a; // For all integer values except the leftmost value ... for (int i = 1; i  0 && x[j - 1] > a) { // Shift left value -- x[j - 1] -- one position to its right -- // x[j]. x[j] = x[j - 1]; // Update insert position to shifted value's original position // (one position to the left). j--; } // Insert a at insert position (which is either the initial insert // position or the final insert position), where a is greater than // or equal to all values to its left. x[j] = a; } assert isSorted(x): "array not sorted"; } }

Listing 4 presents a sort() helper method that uses the insertion sort algorithm to sort an array of integer values. I’ve used assert to check the postcondition of x being sorted before sort() returns to its caller.

The example in Listing 4 demonstrates an important characteristic of assertions, which is that they’re typically expensive to execute. For this reason, assertions are usually disabled in production code. In Listing 4, isSorted() must scan through the entire array, which can be time-consuming in the case of a lengthy array.

Assertions vs. exceptions in Java

Developers use assertions to document logically impossible situations and detect errors in their programming logic. At runtime, an enabled assertion alerts a developer to a logic error. The developer refactors the source code to fix the logic error and then recompiles this code.

Developers use Java’s exception mechanism to respond to non-fatal (e.g., running out of memory) runtime errors, which may be caused by environmental factors, such as a file not existing, or by poorly written code, such as an attempt to divide by 0. An exception handler is often written to gracefully recover from the error so that the program can continue to run.

Assertions are no substitute for exceptions. Unlike exceptions, assertions don’t support error recovery (assertions typically halt program execution immediately — AssertionError isn’t meant to be caught); they are often disabled in production code; and they typically don’t display user-friendly error messages (although this isn’t an issue with assert). It’s important to know when to use exceptions rather than assertions.

When to use exceptions

Suppose you’ve written a sqrt() method that calculates the square root of its argument. In a non-complex number context, it’s impossible to take the square root of a negative number. Therefore, you use an assertion to fail the method if the argument is negative. Consider the following code fragment:

public double sqrt(double x) { assert x >= 0 : "x is negative"; // ... }

It’s inappropriate to use an assertion to validate an argument in this public method. An assertion is intended to detect errors in programming logic and not to safeguard a method from erroneous arguments. Besides, if assertions are disabled, there is no way to deal with the problem of a negative argument. It’s better to throw an exception, as follows:

public double sqrt(double x) { if (x < 0) throw new IllegalArgumentException("x is negative"); // ... }

The developer might choose to have the program handle the illegal argument exception, or simply propagate it out of the program where an error message is displayed by the tool that runs the program. Upon reading the error message, the developer can fix whatever code led to the exception.

Anda mungkin menyedari perbezaan halus antara penegasan dan logik pengesanan ralat. Penegasan menguji x >= 0, sedangkan logik pengesanan ralat menguji x < 0. Penegasan itu optimis: Kami menganggap bahawa hujahnya OK. Sebaliknya, logik pengesanan ralat pesimis: Kami menganggap bahawa hujah itu tidak baik. Tegasan mendokumentasikan logik yang betul, sedangkan pengecualian mendokumentasikan kelakuan runtime yang salah.

Dalam tutorial ini anda telah belajar bagaimana menggunakan penegasan untuk mendokumentasikan logik program yang betul. Anda juga telah mengetahui mengapa penegasan bukan pengganti pengecualian, dan anda telah melihat contoh di mana menggunakan pengecualian akan lebih berkesan.

Kisah ini, "Cara menggunakan penegasan di Java" awalnya diterbitkan oleh JavaWorld.