Как да използвам твърдения в Java

Писането на програми, които работят правилно по време на изпълнение, може да бъде предизвикателство. Това е така, защото нашите предположения за това как ще се държи нашият код при изпълнение често са погрешни. Използването на функцията за твърдения на Java е един от начините да проверите дали вашата логика на програмиране е здрава.

Този урок представя твърдения за Java. Първо ще научите какво са твърдения и как да ги посочите и използвате в кода си. След това ще откриете как да използвате твърдения за налагане на предварителни и последващи условия. И накрая, ще сравните твърденията с изключения и ще разберете защо имате нужда и от двете във вашия код.

изтегляне Вземете кода Изтеглете изходния код за примери в този урок. Създадено от Jeff Friesen за JavaWorld.

Какво представляват твърденията на Java?

Преди JDK 1.4 разработчиците често използваха коментари, за да документират предположения за коректността на програмата. Коментарите са безполезни като механизъм за тестване и отстраняване на грешки в предположенията. Компилаторът игнорира коментарите, така че няма начин да ги използва за откриване на грешки. Разработчиците също често не актуализират коментари, когато променят кода.  

В JDK 1.4 твърденията бяха въведени като нов механизъм за тестване и отстраняване на грешки в предположения за нашия код. По същество твърденията  са компилируеми обекти, които се изпълняват по време на изпълнение, ако приемем, че сте ги активирали за тестване на програми. Можете да програмирате твърдения, за да ви уведомяват за грешки, при които се появяват грешки, значително намалявайки времето, което иначе бихте отделили за отстраняване на грешки в неуспешна програма.

Твърденията се използват за кодифициране на изискванията, които правят програмата правилна или не, чрез тестване на условия (булеви изрази) за истински стойности и уведомяване на разработчика, когато тези условия са неверни. Използването на твърдения може значително да увеличи вашата увереност в коректността на вашия код.

Как да напиша твърдение в Java

Твърденията се реализират чрез assertоператора и java.lang.AssertionErrorкласа. Това изявление започва с ключовата дума assertи продължава с булев израз. Той се изразява синтактично, както следва:

твърди BooleanExpr ;

Ако BooleanExprоценява на true, нищо не се случва и изпълнението продължава. Ако изразът се изчислява на false, обаче AssertionErrorсе създава екземпляр и се изхвърля, както е показано в листинг 1.

Листинг 1:AssertDemo.java (версия 1)

публичен клас AssertDemo {публична статична void main (String [] args) {int x = -1; твърди x> = 0; }}

Твърдението в Листинг 1 показва убеждението на разработчика, че променливата xсъдържа стойност, която е по-голяма или равна на 0. Това обаче очевидно не е така; изпълнението на assertизявлението води до хвърляне AssertionError.

Компилирайте списък 1 ( javac AssertDemo.java) и го стартирайте с активирани твърдения ( java -ea AssertDemo). Трябва да наблюдавате следния изход:

Изключение в нишка "main" java.lang.AssertionError at AssertDemo.main (AssertDemo.java:6)

Това съобщение е малко загадъчно, тъй като не идентифицира какво е причинило AssertionErrorхвърлянето. Ако искате по-информативно съобщение, използвайте assertизявлението, изразено по-долу:

твърди BooleanExpr : expr ;

Тук exprе всеки израз (включително извикване на метод), който може да върне стойност - не можете да извикате метод с тип voidвръщане. Полезен израз е низов литерал, който описва причината за неуспех, както е показано в листинг 2.

Листинг 2:AssertDemo.java (версия 2)

публичен клас AssertDemo {публична статична void main (String [] args) {int x = -1; твърдя x> = 0: "x <0"; }}

Компилирайте списък 2 ( javac AssertDemo.java) и го стартирайте с активирани твърдения ( java -ea AssertDemo). Този път трябва да наблюдавате следната леко разширена продукция, която включва причината за хвърленото AssertionError:

Изключение в нишка "main" java.lang.AssertionError: x <0 при AssertDemo.main (AssertDemo.java:6)

И за двата примера изпълнението AssertDemoбез опцията -ea(разрешаване на твърдения) не води до изход. Когато твърдения не са активирани, те не се изпълняват, въпреки че все още присъстват във файла на класа.

Предпоставки и предпоставки

Утвържденията тестват предположенията на програмата, като проверяват, че нейните различни предварителни условия и последващи условия не са нарушени, като предупреждават разработчика, когато възникне нарушение:

  • А предпоставка е състояние, което трябва да се оцени като вярно преди изпълнението на част от кода последователност. Предпоставките гарантират, че обаждащите се спазват договорите си с повиканите.
  • А postcondition е състояние, което трябва да направи оценка на истинската след изпълнението на част от кода последователност. Последващите условия гарантират, че повиканите спазват договорите си с повикващите.

Предпоставки

Можете да наложите предварителни условия на публични конструктори и методи, като направите изрични проверки и хвърляте изключения, когато е необходимо. За частни помощни методи можете да приложите предпоставки, като посочите твърдения. Помислете за списък 3.

Листинг 3:AssertDemo.java (версия 3)

импортиране на java.io.FileInputStream; импортиране на java.io.InputStream; импортиране на java.io.IOException; class PNG {/ ** * Създайте PNG екземпляр, прочетете определен PNG файл и го декодирайте * в подходящи структури. * * @param filespec път и име на PNG файл за четене * * @throws NullPointerException, когато filespecе *null* / PNG (String filespec) хвърля IOException {// Прилагане на предварителни условия в не частни конструктори и // методи. ако (filespec == null) хвърли нов NullPointerException ("filespec е null"); опитайте (FileInputStream fis = нов FileInputStream (filespec)) {readHeader (fis); }} private void readHeader (InputStream is) хвърля IOException {// Потвърдете, че условието е изпълнено в методите private // helper. assert is! = null: "null предаден на is"; }} публичен клас AssertDemo {public static void main (String [] args) хвърля IOException {PNG png = нов PNG ((args.length == 0?? null: args [0]); }}

В PNGкласа на Обява 3 е минималната началото на библиотека за четене и декодиране PNG (Portable Network графики) файлове с изображения. Конструкторът изрично сравнява filespecс nullхвърляне, NullPointerExceptionкогато този параметър съдържа null. Въпросът е да се наложи предпоставката, която filespecне съдържа null.

Не е подходящо да се уточнява, assert filespec != null;тъй като предпоставката, спомената в Javadoc на конструктора, не би била (технически) изпълнена, когато твърденията бяха деактивирани. (Всъщност би било удостоено, защото FileInputStream()би хвърлило NullPointerException, но не бива да разчитате на поведение без документи.)

Въпреки това, assertе подходящо в контекста на частния readHeader()метод помощник, който ще бъде завършен в края на краищата да се чете и декодира 8-байт с глава на PNG файл е. Предпоставката, която isвинаги се предава ненулева стойност, винаги ще се запази.

Постусловия

Постусловията обикновено се посочват чрез твърдения, независимо дали методът (или конструкторът) е публичен или не. Помислете за списък 4.

Листинг 4:AssertDemo.java (версия 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.

Може да сте забелязали фина разлика между твърдението и логиката за откриване на грешки. Тестовете за твърдение x >= 0, докато логическите тестове за откриване на грешки x < 0. Твърдението е оптимистично: Предполагаме, че аргументът е наред. За разлика от това логиката за откриване на грешки е песимистична: Предполагаме, че аргументът не е наред. Твърденията документират правилна логика, докато изключенията документират неправилно поведение по време на изпълнение.

В този урок сте научили как да използвате твърдения, за да документирате правилната програмна логика. Също така научихте защо твърденията не са заместител на изключенията и видяхте пример, при който използването на изключение би било по-ефективно.

Тази история „Как да използвам твърдения в Java“ първоначално е публикувана от JavaWorld.