Programování

Jak používat tvrzení v Javě

Psaní programů, které za běhu fungují správně, může být náročné. Je to proto, že naše předpoklady o tom, jak se bude náš kód chovat při spuštění, jsou často chybné. Použití funkce tvrzení Java je jedním ze způsobů, jak ověřit, zda je vaše programovací logika v pořádku.

Tento výukový program představuje tvrzení Java. Nejprve se dozvíte, jaké jsou tvrzení a jak je v kódu určit a použít. Dále zjistíte, jak používat tvrzení k vynucování předběžných a dodatečných podmínek. Nakonec porovnáte tvrzení s výjimkami a zjistíte, proč oba potřebujete ve svém kódu.

stáhnout Získat kód Stáhněte si zdrojový kód pro příklady v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.

Co jsou tvrzení Java?

Před JDK 1.4 vývojáři často používali komentáře k dokumentaci předpokladů o správnosti programu. Komentáře jsou však k ničemu jako mechanismus pro testování a ladění předpokladů. Kompilátor ignoruje komentáře, takže neexistuje způsob, jak je použít k detekci chyb. Vývojáři také často neaktualizují komentáře při změně kódu.

V JDK 1.4 byly zavedeny tvrzení jako nový mechanismus pro testování a ladění předpokladů o našem kódu. V podstatě, tvrzení jsou kompilovatelné entity, které se spouštějí za běhu, za předpokladu, že jste jim povolili testování programu. Můžete naprogramovat tvrzení, která vás upozorní na chyby, kde se chyby vyskytnou, čímž výrazně snížíte čas, který byste jinak strávili laděním selhávajícího programu.

Tvrzení se používají ke kodifikaci požadavků, díky nimž je program správný nebo ne testováním podmínky (Booleovské výrazy) pro skutečné hodnoty a upozornění vývojáře, když jsou takové podmínky nepravdivé. Použití tvrzení může výrazně zvýšit vaši důvěru ve správnost kódu.

Jak psát tvrzení v Javě

Tvrzení jsou implementována prostřednictvím tvrdit prohlášení a java.lang.AssertionError třída. Toto prohlášení začíná klíčovým slovem tvrdit a pokračuje booleovským výrazem. Vyjadřuje se syntakticky takto:

tvrdit BooleanExpr;

Li BooleanExpr vyhodnotí jako true, nic se neděje a provádění pokračuje. Pokud je výraz vyhodnocen jako nepravdivý, AssertionError je vytvořena instance a vyvolána, jak je ukázáno v seznamu 1.

Výpis 1:AssertDemo.java (verze 1)

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

Tvrzení v seznamu 1 naznačuje víru vývojáře v tuto proměnnou X obsahuje hodnotu, která je větší nebo rovna 0. To však zjevně není tento případ; the tvrdit provádění příkazu má za následek vyhodení AssertionError.

Zkompilovat výpis 1 (javac AssertDemo.java) a spusťte jej s povolenými tvrzeními (java -ea AssertDemo). Měli byste dodržovat následující výstup:

Výjimka ve vlákně „main“ java.lang.AssertionError at AssertDemo.main (AssertDemo.java:6)

Tato zpráva je poněkud záhadná v tom, že neidentifikuje, co způsobilo AssertionError být hozen. Pokud chcete více informativní zprávu, použijte tvrdit prohlášení vyjádřené níže:

tvrdit BooleanExpr : expr;

Tady, expr je libovolný výraz (včetně vyvolání metody), který může vrátit hodnotu - nemůžete vyvolat metodu pomocí a prázdnota návratový typ. Užitečným výrazem je řetězcový literál, který popisuje důvod selhání, jak je ukázáno v seznamu 2.

Výpis 2:AssertDemo.java (verze 2)

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

Zkompilovat výpis 2 (javac AssertDemo.java) a spusťte jej s povolenými tvrzeními (java -ea AssertDemo). Tentokrát byste měli sledovat následující mírně rozšířený výstup, který zahrnuje důvod vyvolání AssertionError:

Výjimka ve vlákně "main" java.lang.AssertionError: x <0 na AssertDemo.main (AssertDemo.java:6)

U obou příkladů běží AssertDemo bez -ea Možnost (povolit tvrzení) nemá za následek žádný výstup. Pokud nejsou povoleny kontrolní výrazy, nebudou provedeny, přestože jsou stále přítomny v souboru třídy.

Předpoklady a dodatečné podmínky

Tvrzení testují předpoklady programu tím, že ověřují, zda nejsou porušeny jeho různé předběžné a následné podmínky, a upozorní vývojáře, když dojde k porušení:

  • A předpoklad je podmínka, která musí být vyhodnocena jako true před provedením nějaké sekvence kódu. Předpoklady zajišťují, aby volající dodržovali své smlouvy s volanými.
  • A podmínka je podmínka, která musí být vyhodnocena jako true po provedení nějaké sekvence kódu. Postconditions zajišťují, že volající dodržují své smlouvy s volajícími.

Předpoklady

Můžete vynutit předběžné podmínky u veřejných konstruktorů a metod provedením explicitních kontrol a v případě potřeby vyvoláním výjimek. U soukromých pomocných metod můžete vynutit předběžné podmínky zadáním tvrzení. Zvažte výpis 3.

Výpis 3:AssertDemo.java (verze 3)

import java.io.FileInputStream; importovat java.io.InputStream; import java.io.IOException; třída PNG {/ ** * Vytvořte instanci PNG, přečtěte si zadaný soubor PNG a dekódujte jej * do vhodných struktur. * * @param filespec cesta a název souboru PNG ke čtení * * @throws NullPointerException když soubory je * nula * / PNG (String filespec) vyvolá IOException {// Vynucuje předpoklady v neveřejných konstruktorech a // metodách. if (filespec == null) hodit novou NullPointerException ("filespec je null"); try (FileInputStream fis = new FileInputStream (filespec)) {readHeader (fis); }} private void readHeader (InputStream is) throws IOException {// Potvrďte, že předpoklad je splněn v soukromých // pomocných metodách. tvrdit je! = null: "předána hodnota null je"; }} public class AssertDemo {public static void main (String [] args) hodí IOException {PNG png = new PNG ((args.length == 0)? null: args [0]); }}

The PNG třída v seznamu 3 je minimální začátek knihovny pro čtení a dekódování obrazových souborů PNG (přenosná síťová grafika). Konstruktor explicitně porovnává soubory s nula, házení NullPointerException když tento parametr obsahuje nula. Jde o to prosadit předpoklad, že soubory neobsahuje nula.

Není vhodné specifikovat prosazovat soubory spec! = null; protože podmínka uvedená v Javadocu konstruktéra by (technicky) nebyla dodržena, když byla tvrzení deaktivována. (Ve skutečnosti by to bylo poctěno, protože FileInputStream () by hodil NullPointerException, ale neměli byste se spoléhat na nezdokumentované chování.)

Nicméně, tvrdit je vhodné v kontextu soukromého readHeader () pomocná metoda, která bude nakonec dokončena ke čtení a dekódování 8bajtového záhlaví souboru PNG. Předpoklad, že je vždy předán, nenulová hodnota bude vždy držet.

Postkondice

Postkondice se obvykle zadávají pomocí tvrzení, bez ohledu na to, zda je metoda (nebo konstruktor) veřejná. Zvažte výpis 4.

Výpis 4:AssertDemo.java (verze 4)

public class AssertDemo {public static void main (String [] args) {int [] array = {20, 91, -6, 16, 0, 7, 51, 42, 3, 1}; sort (pole); 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; návrat true; } private static void sort (int [] x) {int j, a; // Pro všechny celočíselné hodnoty kromě hodnoty zcela vlevo ... pro (int i = 1; i 0 && x [j - 1]> a) {// Posunout levou hodnotu - x [j - 1] - jedna pozice napravo - // x [j]. x [j] = x [j - 1]; // Aktualizuje pozici vložky na původní pozici posunuté hodnoty // (o jednu pozici vlevo). j--; } // Vložte pozici at insert (což je buď počáteční poloha // // pozice nebo konečná pozice insertu), kde a je větší než // nebo rovno všem hodnotám nalevo. x [j] = a; } prosadit isSorted (x): "pole není tříděno"; }}

Výpis 4 představuje a sort () pomocná metoda, která používá třídění vložení algoritmus pro třídění pole celočíselných hodnot. Použil jsem tvrdit zkontrolovat dodatečnou podmínku X se třídí dříve sort () se vrátí ke svému volajícímu.

Příklad v seznamu 4 ukazuje důležitou charakteristiku tvrzení, která spočívá v tom, že jejich provedení je obvykle nákladné. Z tohoto důvodu jsou tvrzení v produkčním kódu obvykle zakázána. V seznamu 4, isSorted () musí prohledávat celé pole, což může být v případě dlouhého pole časově náročné.

Tvrzení vs. výjimky v Javě

Vývojáři pomocí tvrzení dokumentují logicky nemožné situace a detekují chyby v jejich programovací logice. Za běhu povolené tvrzení upozorní vývojáře na logickou chybu. Vývojář refaktoruje zdrojový kód, aby opravil logickou chybu, a poté tento kód znovu zkompiluje.

Vývojáři používají mechanismus výjimek Java k reakci na nezávažné (např. Vyčerpání paměti) runtime chyby, které mohou být způsobeny faktory prostředí, jako je neexistující soubor, nebo špatně napsaným kódem, například pokusem o rozdělení 0. Obslužná rutina výjimky je často zapsána, aby se elegantně zotavila z chyby, aby mohl program pokračovat v běhu.

Tvrzení nenahrazují výjimky. Na rozdíl od výjimek tvrzení nepodporují zotavení po chybě (tvrzení obvykle okamžitě zastaví spuštění programu -AssertionError nemá být chycen); v produkčním kódu jsou často deaktivovány; a obvykle nezobrazují uživatelsky přívětivé chybové zprávy (i když to není problém tvrdit). Je důležité vědět, kdy použít spíše výjimky než tvrzení.

Kdy použít výjimky

Předpokládejme, že jste napsali sqrt () metoda, která vypočítá druhou odmocninu svého argumentu. V kontextu nekomplexního čísla je nemožné vzít druhou odmocninu záporného čísla. Proto použijete tvrzení k selhání metody, pokud je argument záporný. Zvažte následující fragment kódu:

public double sqrt (double x) {assert x> = 0: "x je záporné"; // ...}

V tomto případě není vhodné použít tvrzení k ověření argumentu veřejnost metoda. Účelem tvrzení je detekovat chyby v programovací logice a ne chránit metodu před chybnými argumenty. Kromě toho, pokud jsou tvrzení deaktivována, neexistuje způsob, jak se vypořádat s problémem negativního argumentu. Je lepší vyvolat výjimku následujícím způsobem:

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

Vývojář se může rozhodnout, že program zpracuje výjimku s nelegálním argumentem, nebo ji jednoduše rozšíří z programu, kde se chybová zpráva zobrazí nástrojem, který program spouští. Po přečtení chybové zprávy může vývojář opravit jakýkoli kód, který vedl k výjimce.

Možná jste si všimli jemného rozdílu mezi tvrzením a logikou detekce chyb. Testy tvrzení x> = 0, zatímco logické testy detekce chyb x <0. Tvrzení je optimistické: Předpokládáme, že argument je v pořádku. Naproti tomu logika detekce chyb je pesimistická: Předpokládáme, že argument není v pořádku. Tvrzení dokumentují správnou logiku, zatímco výjimky dokumentují nesprávné chování za běhu.

V tomto kurzu jste se naučili, jak používat tvrzení k dokumentaci správné logiky programu. Zjistili jste také, proč tvrzení nenahrazují výjimky, a viděli jste příklad, kdy by použití výjimky bylo efektivnější.

Tento příběh „Jak používat tvrzení v Javě“ původně publikoval JavaWorld.