O inicializovaní kolekcií a null pri metódach, ktoré ich majú v návratových typoch

Celý článok by sa dal skrátiť do dvoch sloganov:

Ak metóda vracia kolekciu (List, Set…), nikdy nevracajte null! Inštančné premenné, ktoré sú kolekcie vždy inicializujte! Nikdy nech nie sú null!

Dlhá verzia

V jednu pražskú piatkovú noc (bola zima…) sme márne hľadali voľný podnik. Blúdili sme pol hodinu od Karláku až k Míráku od dverí k dverám, ale nikde nebolo miesta ani pre jedného, tobôž pre štyroch. Na poslednú chvíľu si domorodec M. spomenul, že pozná naozaj skvelý podnik(TM), ale je to trochu ďalej. Čo už, vydržali sme hodinu, vydržíme ešte desať minút. I tak bolo: po desiatich minútach sme mali možnosť vidieť vytúžený vchod.

S nápisom “Večerka”.

“Ale… ale… ešte pred dvoma týždňami to tu bolo!” bránil sa M.

Už sme nemuseli riešit, či je podnik plný; žiadny tam totiž nebol. Pred nami bolo ešte dvadsať minút ďalšieho pochodu.

Situácia v Jave

Takáto istá situácia (bez pochodu smrti) sa môže stať v metóde, ktorá pristupuje k premennej kolekcie: teda tradične typu List, Set a podobne.

Predstavme si triedu so šporiacim prasiatkom: môžeme doňho vložiť mincu a prípadne sa pozrieť, aké mince sa v ňom nachádzajú.

piggy-bank

public class PiggyBank {

    private List<Coin> coins;

    public void insert(Coin coin) {
        coins.add(coin);
    }

    public List<Coin> getCoins() {
        return this.allCoins;
    }

    public static enum Coin {
        ONE_CENT, TWO_CENT, FIVE_CENT, TEN_CENT, TWENTY_CENT, FIFTY_CENT,
        ONE_EURO, TWO_EURO      
    }       
}

Toto je nešťastná implementácia. Vytvorme si nové prasiatko a skúsme zistiť, či je prázdne.

PiggyBank piggyBank = new PiggyBank();
if(piggyBank.getCoins().isEmpty()) {
    // prasiatko je prázdne
}

Okamžite dostaneme po hlave výnimkou NullPointerException.

Metóda getCoins() pristupuje k premennej coins, ktorá je null, a tú vráti z metódy. Samozrejme, zistovať, prázdnotu pomocou isEmpty() na nullovom objekte nepôjde.

Úbohý klient triedy PiggyBank je odsúdený na testovanie dvoch prípadov:

  • je vôbec kolekcia mincí nenullová?
  • ak áno, je prázdna?

To je však veľmi nešťastné a zbytočné. A nelogické. V 95% operácií (číslo vytiahnuté z brucha) pracujete so zoznamom. Buď je prázdny alebo má nejaké prvky, s ktorými chcete pracovať. Situácia s null zoznamom znamená, že “nemám ani ten zoznam”.

Nielenže sa musíme pozerať, či je v podniku nejaký voľný stôl (očami iterujem zoznam stolov), ale ešte predtým som nútený zisťovať, či náhodou môj obľúbený podnik stojí, nebodaj, či budovu nezničili buldozérom.

java-bar

Preto zásada znie:

Ak metóda vracia kolekciu (List, Set…), nikdy nevracajte null

V tomto prípade je páchateľom neinicializovaná inštančná premenná coins. (To však klienta triedy nezaujíma, lebo je to záležitosť internej implementácie.) To vedie k druhej zásade:

Inštančné premenné, ktoré sú kolekcie (listy, sety, …) vždy inicializujte! Nikdy nech nie sú null!

Najlepšia možnosť je dostať do prstov reflex:

List<Coin> coins = new ArrayList<Coin>()

alebo:

List<Coin> coins = new LinkedList<Coin>()

alebo hocičo iné, kde na pravej strane je inicializácia kolekcie.

Toto je vždy podozrivé:

List<Coin> coins;

Ak ide o inštančnú premennú, v núdzi možno kolekciu nainicializovať aj v konštruktore:

public class PiggyBank {        
    private List<Coin> coins;
    public PiggyBank() {
         coins = new ArrayList<Coin>();         
    }

ale omnoho lepšie je to mať pekne pohromade.

Keď máme garantované, že kolekcia nie je nikdy null, uľahčíme tým uvažovanie pre klientov triedy, ktorí nemusia čakať náhodné a nečakané prekvapenia, či dokonca NullPointerExceptiony

To isté pre polia

To isté platí aj pre polia, ktoré sú maskovanými objektami. Tento výraz je tiež podozrivý:

private Coin[] coins;

Namiesto neho omnoho radšej:

private static final int DEFAULT_CAPACITY;

private Coin[] coins = new Coin[DEFAULT_CAPACITY];

(Teraz opomeniem, že toto je očividný prípad, keď namiesto poľa je omnoho prehľadnejšie použiť zoznam.)

Rovnako sa vždy uistite, že vaša metóda s návratovým typom typu pole nevracia null: spôsobí to totiž rovnaké prekvapenie ako v prípade kolekcií.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *