ComiaTip static final int vs. enum


static final int vs. enum

Kirjoittanut J-P Julkaistu 5.3.2009

On yksi asia mihin jokainen koodari tulee varmasti törmäämään ohjelmointikielestä riippumatta: vakiot. Vakioiden käyttötavat ovat lukuisat, mutta valitettavan usein törmään koodiin, jossa on käytetty vakioita silloin, kun vakioiden käyttö ei ole suotavaa. Älkääkä nyt käsittäkö väärin, minä pidän vakioista. Vakio on aina parempi ratkaisu kuin koodin joukosta löytyvä taikanumero tai taikastringi (eng. magic number ja magic String). Taikanumeroita, ja kaikkea niistä seuraavaa pahaa, käsittelen kuitenkin myöhemmässä ComiaTipsissä. Nyt on tarkoitukseni vaahdota vakioiden väärinkäytöstä.

Vakioluettelot

Ennen Javan viitosversiota oli hyvin tyypillistä käyttää kokonaislukuvakioita määriteltäessä ennaltatunnettua arvojoukkoa. Esimerkiksi pelikorttien maat oltaisiin voitu määritellä seuraavasti:

public final int CARD_COLOR_DIAMONDS=1; public final int CARD_COLOR_HEARTS=2; public final int CARD_COLOR_SPADES=3; public final int CARD_COLOR_CROSSES=4;

Menetelmässä on kuitenkin useita haittapuolia, joista ehkä merkittävimmistä on tyyppiturvallisuuden puuttuminen. Metodille (setCardColor(int color)), joka odottaa saavansa parametrina kelvollisen pelikortin maakoodin, voidaan antaa mikä tahansa kokonaisluku, eikä kääntäjä varoita koodaria tällaisesta virheestä. Toinen merkittävä ongelma esiintyy mikäli vakiot on sisällytetty luokkakirjastoon, jolla on useita käyttäjiä. Jos luokkakirjaston sisältämän vakion arvoa muutetaan on kaikki luokkakirjastoa käyttävä koodi käännettävä uudelleen. Miksi? Kääntäjä sijoittaa luokkakirjaston sisältämän vakioarvon arvona sitä käyttävään koodiin, eikä viittauksena alkuperäiseen luokkakirjastoon. Jos siis luokkakirjaston sisältämän vakion arvo muuttuu, ei tämä muutos päivity kirjaston käyttäjille ennen koodin uudelleen kääntämistä.

Enumeraatiot

Javan viitosversio toi Javaan, kaiken muun hyvän ohella, myös kauan kaivatut enumeraatiot. Enumeraatioiden avulla voidaan välttää useimmat vakioluetteloista aiheutuvat ongelmat. Esimerkiksi edellä esitelty pelikorttien maa voitaisiin esittää seuraavasti:

enum CardColor { DIAMONDS, HEARTS, SPADES, CROSSES }

Vastaavasti metodi, jolle on annettava parametrina pelikortin maa tulisi nyt muotoon setCardColor(CardColor color). Metodille voidaan antaa vain CardColor tyyppisiä arvoja ja CardColor saa ainoastaan arvot CardColor.DIAMONDS, CardColor.HEARTS, CardColor.SPADES ja CardColor.CROSSES. Jos yrität antaa metodille mitä tahansa muita arvoja koodisi ei käänny ja virheet löytyvät nopeasti ja helposti.

Useimmista muista kielistä poiketen Javan enumeraatiot toimivat hyvin samalla tavalla kuin luokatkin, niille voidaan määritellä rakentajia, attribuutteja ja metodeja. Toisin kuin muita luokkia enumeraatioita voi käyttää myös switch-case rakenteessa.

enum CardColor { DIAMONDS("diamond.png"), HEARTS("heart.png"), SPADES("spade.png"), CROSSES("cross.png"); private final String iconFileName; CardColor(String iconFileName) { this.iconFileName=iconFileName; } public String getIconFileName() { return iconFileName; } }

Enumeraatioilla on myös valmiina muutamia käteviä metodeja. values (esim. CardColor.values()) palauttaa kaikki enumeraation arvot taulukkona (CardColor[]). toString (esim. CardColor.DIAMONDS.toString()) palauttaa oletusarvoisesti enumeraation nimen ("DIAMONDS"). Mikäli toString -metodin oletustoteutus ei miellytä voidaan se ylikirjoittaa. valueOf(String) palauttaa enumeraatioinstanssin, jonka nimi vastaa parametrina annettua merkkijonoa (esim. CardColor.valueOf("DIAMONDS") palauttaa arvon CardColor.DIAMONDS).

Aika ennen Java 5:sta

Jos jostain syystä käytät Java 5:sta vanhempaa Java versiota, voit silti välttää vakioluetteloita. Käytä tavallisia luokkia, joilla on private rakentaja ja static final kenttiä. Tämä ei ole ihan sama kuin enumeraatio, et voi käyttää luokkaa esimerkiksi switch-case rakenteessa, mutta parempi ratkaisu kuitenkin kuin public static final int. Tällä tavalla määriteltynä pelikorttien maa voitaisiin esittää seuraavasti:

public final class CardColor { public static final CardColor DIAMONDS=new CardColor(); public static final CardColor HEARTS=new CardColor(); public static final CardColor SPADES=new CardColor(); public static final CardColor CROSSES=new CardColor(); private final CardColor() {} }

Nyrkkisääntö

Nyrkkisääntönä esittäisin kaikille koodareille: Jos tiedät koko arvojoukon jo koodaus vaiheessa, eikä uusia arvoja voi syntyä ohjelman ajon aikana, käytä enumeraatiota kokonaislukuvakioiden sijasta. Samalla perusteella suosittelen pysymään erossa myös merkkijonovakioista.

Lisää enumeraatioista Javassa ja muissa kielissä kurssilla Suunnittelumallit ja refaktorointi.