Vyhnite sa kontrole nulového vyhlásenia v Jave

1. Prehľad

Spravidla nulový premenné, odkazy a kolekcie sú v kóde Java zložité. Nielen, že sú ťažko identifikovateľné, ale aj komplexné.

V skutočnosti bude každá slečna pri jednaní s nulový nie je možné identifikovať v čase zostavenia a vedie k a NullPointerException za behu.

V tomto tutoriáli sa pozrieme na to, čo je potrebné skontrolovať nulový v Jave a rôzne alternatívy, ktoré nám pomáhajú vyhnúť sa nulový kontroluje náš kód.

2. Čo je NullPointerException?

Podľa Javadoc pre NullPointerException, je vyhodené, keď sa aplikácia pokúsi použiť nulový v prípade, že sa vyžaduje predmet, ako napríklad:

  • Volanie inštančnej metódy a nulový objekt
  • Prístup alebo úprava poľa a nulový objekt
  • Vzhľadom na dĺžku nulový akoby to bolo pole
  • Pristupovanie alebo úprava slotov aplikácie nulový akoby to bolo pole
  • Vhadzovanie nulový akoby to bolo a Hoditeľné hodnotu

Pozrime sa rýchlo na niekoľko príkladov kódu Java, ktoré spôsobujú túto výnimku:

public void doSomething () {Výsledok reťazca = doSomethingElse (); if (result.equalsIgnoreCase ("Success")) // success}} private String doSomethingElse () {return null; }

Tu, skúsili sme vyvolať volanie metódy pre a nulový odkaz. To by malo za následok a NullPointerException.

Ďalším bežným príkladom je, ak sa pokúsime získať prístup k a nulový pole:

public static void main (String [] args) {findMax (null); } private static void findMax (int [] arr) {int max = arr [0]; // skontrolovať ďalšie prvky v slučke}

To spôsobí a NullPointerException na riadku 6.

Prístup k ľubovoľnému poľu, metóde alebo indexu a nulový predmet spôsobuje a NullPointerException, ako je zrejmé z vyššie uvedených príkladov.

Bežný spôsob, ako sa vyhnúť NullPointerException je skontrolovať nulový:

public void doSomething () {Výsledok reťazca = doSomethingElse (); if (result! = null && result.equalsIgnoreCase ("Success")) {// success} else // failure} private String doSomethingElse () {return null; }

V skutočnom svete je pre programátorov ťažké určiť, ktoré objekty môžu byť nulový. Agresívne bezpečnou stratégiou môže byť kontrola nulový pre každý objekt. To však spôsobuje veľa zbytočností nulový kontroluje a robí náš kód menej čitateľným.

V nasledujúcich niekoľkých častiach si prejdeme niektoré alternatívy v Jave, ktoré sa vyhnú takejto redundancii.

3. Manipulácia nulový Prostredníctvom zmluvy o API

Ako je uvedené v poslednej časti, prístup k metódam alebo premenným systému nulový predmety spôsobuje a NullPointerException. Diskutovali sme tiež o tom, že uvedenie a nulový skontrolovať objekt pred prístupom, vylučuje možnosť NullPointerException.

Často však existujú API, ktoré zvládnu nulový hodnoty. Napríklad:

public void print (parameter objektu) {System.out.println ("tlač" + parameter); } public Object process () hodí výnimku {Object result = doSomething (); if (result == null) {throw new Exception ("Processing failed. Got a null response"); } else {návrat výsledok; }}

The print () volanie metódy by sa iba vytlačilo "nulový" ale nebude hádzať výnimku. Podobne proces () by sa nikdy nevrátil nulový vo svojej odpovedi. To skôr hodí Výnimka.

Takže pre kód klienta, ktorý pristupuje k vyššie uvedeným API, nie je potrebné a nulový skontrolovať.

Takéto API ich však musia vo svojej zmluve výslovne uviesť. Spoločným miestom API na zverejnenie takejto zmluvy je JavaDoc.

To však dáva žiadny jasný údaj o zmluve o API, a preto sa spolieha na vývojárov klientskeho kódu, aby zabezpečili jeho súlad.

V nasledujúcej časti uvidíme, ako s tým vývojárom pomôže niekoľko IDE a ďalšie vývojové nástroje.

4. Automatizácia zmlúv API

4.1. Používanie statickej analýzy kódu

Nástroje na statickú analýzu kódu pomáhajú do značnej miery vylepšiť kvalitu kódu. Niekoľko takýchto nástrojov tiež umožňuje vývojárom udržiavať nulový zmluva. Jedným z príkladov sú FindBugs.

FindBugs pomáha spravovať nulový zmluva cez @ Nullable a @NonNull anotácie. Tieto anotácie môžeme použiť na akúkoľvek metódu, pole, lokálnu premennú alebo parameter. Vďaka tomu je pre kód klienta výslovné, či môže byť anotovaný typ nulový alebo nie. Pozrime sa na príklad:

public void accept (@Nonnull Object param) {System.out.println (param.toString ()); }

Tu, @NonNull objasňuje, že argument nemôže byť nulový. Ak kód klienta volá túto metódu bez kontroly argumentu pre nulový, FindBugs vygeneruje varovanie v čase kompilácie.

4.2. Používanie podpory IDE

Vývojári sa pri písaní kódu Java zvyčajne spoliehajú na IDE. A do veľkej miery určite pomáhajú funkcie, ako je dokončenie inteligentného kódu a užitočné varovania, ako napríklad keď premenná nemusí byť priradená.

Niektoré IDE tiež umožňujú vývojárom spravovať zmluvy API, a tým eliminujú potrebu nástroja na analýzu statického kódu. IntelliJ IDEA poskytuje @NonNull a @ Nullable anotácie. Ak chcete pridať podporu pre tieto anotácie do IntelliJ, musíme pridať nasledujúcu závislosť Maven:

 anotácie org.jetbrains 16.0.2 

Teraz, IntelliJ vygeneruje varovanie, ak nulový kontrola chýba, ako v našom poslednom príklade.

IntelliJ tiež poskytuje a Zmluva anotácia pre spracovanie zložitých kontraktov API.

5. Tvrdenia

Doteraz sme hovorili iba o odstránení potreby nulový šeky z klientskeho kódu. To je však v aplikáciách z reálneho sveta zriedka použiteľné.

Teraz poďme Predpokladajme, že pracujeme s API, ktoré nemôže prijať nulový parametre alebo môže vrátiť a nulový odpoveď, ktorú musí vybaviť klient. To predstavuje potrebu skontrolovať parametre alebo reakciu na a nulový hodnotu.

Tu môžeme použiť Java výrazy namiesto tradičných nulový skontrolujte podmienené vyhlásenie:

public void accept (Object param) {assert param! = null; doSomething (param); }

V riadku 2 skontrolujeme, či nulový parameter. Ak sú tvrdenia povolené, viedlo by to k AssertionError.

Aj keď je to dobrý spôsob presadzovania predbežných podmienok, ako súnulový parametre, tento prístup má dva hlavné problémy:

  1. Tvrdenia sú zvyčajne na JVM zakázané
  2. A nepravdivé výsledkom tvrdenia je nekontrolovaná chyba, ktorá je neodstrániteľná

Programátorom sa preto neodporúča používať výrazy na kontrolu podmienok. V nasledujúcich častiach si rozoberieme ďalšie spôsoby zaobchádzania nulový validácie.

6. Vyhýbanie sa Nulový Kontroly prostredníctvom postupov kódovania

6.1. Predpoklady

Je dobrým zvykom písať kód, ktorý zlyhá skôr. Preto, ak API akceptuje viac parametrov, ktoré nesmú byť nulový, je lepšie skontrolovať každýnulový parameter ako predpoklad API.

Pozrime sa napríklad na dve metódy - jednu, ktorá zlyháva skoro, a druhú, ktorá zlyháva:

public void goodAccept (Reťazec jeden, Reťazec dva, Reťazec tri) {if (one == null || two == null || three == null) {throw new IllegalArgumentException (); } proces (jeden); postup (dva); proces (tri); } public void badAccept (Reťazec jeden, Reťazec dva, Reťazec tri) {if (jeden == null) {hodiť nový IllegalArgumentException (); } else {proces (jeden); } if (two == null) {throw new IllegalArgumentException (); } else {proces (dva); } if (three == null) {throw new IllegalArgumentException (); } else {proces (tri); }}

Je zrejmé, že by sme mali uprednostňovať goodAccept () cez badAccept ().

Ako alternatívu môžeme na overenie parametrov API použiť aj predpoklady spoločnosti Guava.

6.2. Používanie primitívov namiesto tried obalov

Odkedy nulový nie je prijateľná hodnota pre primitívne osoby ako int, mali by sme ich uprednostniť pred ich obalovými kolegami ako Celé číslo všade, kde je to možné.

Zvážte dve implementácie metódy, ktorá sčíta dve celé čísla:

public static int primitiveSum (int a, int b) {return a + b; } public static Integer wrapperSum (Integer a, Integer b) {return a + b; }

Teraz nazvime tieto API v našom kóde klienta:

int sum = primitiveSum (null, 2);

To by viedlo k chybe v čase kompilácie nulový nie je platná hodnota pre int.

A keď používame API s obalovými triedami, dostaneme NullPointerException:

assertThrows (NullPointerException.class, () -> wrapperSum (null, 2));

Existujú aj ďalšie faktory pre použitie primitívnych prostriedkov nad obalom, ako sme sa venovali v ďalšom výučbe Java Primitive versus Objects.

6.3. Prázdne zbierky

Príležitostne musíme kolekciu vrátiť ako odpoveď z metódy. Pri takýchto metódach by sme sa mali vždy snažiť vrátiť prázdnu zbierku namiesto nulový:

public List names () {if (userExists ()) {return Stream.of (readName ()). collect (Collectors.toList ()); } else {return Collections.emptyList (); }}

Preto sme sa vyhli potrebe nášho klienta vykonávať a nulový skontrolujte pri volaní tejto metódy.

7. Používanie Predmety

Java 7 predstavila nové Predmety API. Toto API má niekoľko statický úžitkové metódy, ktoré odoberajú veľa nadbytočného kódu. Pozrime sa na jednu takúto metódu, requireNonNull ():

public void akceptovať (parameter objektu) {Objects.requireNonNull (param); // urob niečo() }

Teraz otestujme súhlasiť() metóda:

assertThrows (NullPointerException.class, () -> accept (null));

Takže ak nulový sa odovzdáva ako argument, súhlasiť() hodí a NullPointerException.

Táto trieda má tiež isNull () a nonNull () metódy, ktoré možno použiť ako predikáty na kontrolu objektu nulový.

8. Používanie Voliteľné

8.1. Použitím aleboElseThrow

Java 8 predstavila nový Voliteľné API v jazyku. Toto ponúka lepšiu zmluvu na spracovanie voliteľných hodnôt v porovnaní s nulový. Uvidíme ako Voliteľné odoberá potrebu nulový kontroly:

public Voliteľný proces (boolean spracované) {String response = doSomething (spracované); if (response == null) {return Optional.empty (); } return Optional.of (response); } privátny reťazec doSomething (boolean spracovaný) {if (spracovaný) {návrat "odovzdaný"; } else {return null; }}

Vrátením Voliteľné, ako je uvedené vyššie, the procesu metóda objasňuje volajúcemu, že odpoveď môže byť prázdna a musí byť spracovaná v čase kompilácie.

To predovšetkým odstraňuje potrebu akýchkoľvek nulový skontroluje kód klienta. S prázdnou odpoveďou je možné zaobchádzať rôzne pomocou deklaratívneho štýlu súboru Voliteľné API:

assertThrows (Exception.class, () -> process (false) .orElseThrow (() -> new Exception ()));

Ďalej tiež poskytuje lepšia zmluva s vývojármi API na znak toho, že klientom môže API vrátiť prázdnu odpoveď.

Aj keď sme eliminovali potrebu a nulový skontrolujte volajúceho tohto API, použili sme ho na vrátenie prázdnej odpovede. Aby sa tomu zabránilo, Voliteľné poskytuje ofNullable metóda, ktorá vracia Voliteľné so stanovenou hodnotou alebo prázdny, ak je hodnota nulový:

public Voliteľný proces (boolean spracované) {String response = doSomething (spracované); návrat Optional.ofNullable (odpoveď); }

8.2. Použitím Voliteľné so zbierkami

Pri práci s prázdnymi zbierkami Voliteľné príde vhod:

public String findFirst () {return getList (). stream () .findFirst () .orElse (DEFAULT_VALUE); }

Táto funkcia má vrátiť prvú položku zoznamu. The Prúd API findFirst funkcia vráti prázdny Voliteľné keď nie sú k dispozícii žiadne údaje. Tu sme použili alebo iný namiesto toho poskytnúť predvolenú hodnotu.

To nám umožňuje spracovávať buď prázdne zoznamy, alebo zoznamy, ktoré potom, čo sme použili Prúd knižnica filter metódou, nemám čo dodávať.

Prípadne môžeme klientovi tiež umožniť rozhodnúť sa, ako naloží prázdny návratom Voliteľné z tejto metódy:

public Voliteľné findOptionalFirst () {return getList (). stream () .findFirst (); }

Preto, ak je výsledok getList je prázdny, táto metóda vráti prázdny Voliteľné klientovi.

Použitím Voliteľné s kolekciami nám umožňuje navrhovať API, ktoré určite vrátia nenulové hodnoty, čím sa vyhneme explicitnému nulový kontroly klienta.

Tu je dôležité poznamenať, že sa na túto implementáciu spolieha getList nevracia sa nulový. Ako sme však diskutovali v poslednej časti, často je lepšie vrátiť prázdny zoznam ako a nulový.

8.3. Kombinácia voliteľných doplnkov

Keď začneme vracať svoje funkcie Voliteľné potrebujeme spôsob, ako spojiť ich výsledky do jednej hodnoty. Zoberme si naše getList príklad zo skôr. Čo keby to bolo na vrátenie Voliteľné zoznam alebo mali byť zabalené metódou, ktorá zabalila a nulový s Voliteľné použitím ofNullable?

Náš findFirst metóda chce vrátiť Voliteľné prvý prvok Voliteľné zoznam:

public Optional optionalListFirst () {return getOptionalList () .flatMap (list -> list.stream (). findFirst ()); }

Použitím flatMap funkcia na Voliteľné vrátil sa z getOptional môžeme vybaliť výsledok vnútorného výrazu, ktorý sa vráti Voliteľné. Bez flatMap, výsledok by bol Voliteľné. The flatMap operácia sa vykonáva, iba ak Voliteľné nie je prázdny.

9. Knižnice

9.1. Pomocou Lomboku

Lombok je skvelá knižnica, ktorá v našich projektoch znižuje množstvo štandardných kódov. Dodáva sa so sadou anotácií, ktoré nahrádzajú bežné časti kódu, ktoré si často píšeme v Java aplikáciách, ako sú getre, settery a natiahnuť(), vymenovať zopár.

Ďalšia z jeho anotácií je @NonNull. Ak teda projekt už používa Lombok na odstránenie štandardného kódu, @NonNull môže nahradiť potrebu nulový kontroly.

Predtým, ako si ukážeme niekoľko príkladov, pridajme závislosť Maven pre Lombok:

 org.projectlombok lombok 1.18.6 

Teraz môžeme použiť @NonNull kdekoľvek a nulový je potrebná kontrola:

public void accept (@NonNull Object param) {System.out.println (param); }

Takže sme jednoducho anotovali objekt, pre ktorý nulový bola by vyžadovaná kontrola a Lombok vygeneroval kompilovanú triedu:

public void accept (@NonNull Object param) {if (param == null) {throw new NullPointerException ("param"); } else {System.out.println (param); }}

Ak param je nulový, táto metóda vyvolá a NullPointerException. Metóda to musí vo svojej zmluve uviesť výslovne a kód klienta musí spracovať výnimku.

9.2. Použitím StringUtils

Spravidla String validácia obsahuje okrem kontroly aj prázdnu hodnotu nulový hodnotu. Preto by spoločné vyhlásenie o overení platnosti bolo:

public void accept (reťazec param) {if (null! = param &&! param.isEmpty ()) System.out.println (param); }

To sa rýchlo stane nadbytočným, ak budeme musieť veľa riešiť String typy. Toto je kde StringUtils príde vhod. Predtým, ako to uvidíme v akcii, pridajme závislosť Maven pre commons-lang3:

 org.apache.commons commons-lang3 3.8.1 

Poďme teraz vyššie uvedený kód refaktorovať pomocou StringUtils:

public void accept (String param) {if (StringUtils.isNotEmpty (param)) System.out.println (param); }

Takže sme vymenili naše nulový alebo prázdny šek s a statický úžitková metóda isNotEmpty (). Toto API ponúka ďalšie výkonné obslužné metódy na bežné spracovanie String funkcie.

10. Záver

V tomto článku sme sa pozreli na rôzne dôvody NullPointerException a prečo je ťažké ho identifikovať. Potom sme videli rôzne spôsoby, ako sa vyhnúť redundancii v kóde okolo kontroly nulový s parametrami, návratovými typmi a inými premennými.

Všetky príklady sú k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found