Ohjelmointi

Luo lueteltuja vakioita Javassa

Joukko "lukemattomia vakioita" on järjestetty vakioiden kokoelma, jotka voidaan laskea, kuten numerot. Tämän ominaisuuden avulla voit käyttää niitä kuten numeroita taulukon indeksointiin tai voit käyttää niitä indeksimuuttujana for for -silmukassa. Java-tilassa tällaiset objektit tunnetaan useimmiten nimellä "luetellut vakiot".

Luetteloitujen vakioiden käyttäminen voi tehdä koodista luettavamman. Voit esimerkiksi määrittää uuden tietotyypin nimeltä Väri vakioilla PUNAINEN, VIHREÄ ja SININEN mahdollisina arvoina. Ajatuksena on, että väri on luoma muiden luomiesi objektien, kuten auto-objektien, attribuutti:

 luokan auto {Väri väri; ...} 

Sitten voit kirjoittaa selkeän, luettavan koodin, kuten tämä:

 myCar.color = PUNAINEN; 

jotain tällaista:

 myCar.color = 3; 

Vielä tärkeämpi lueteltujen vakioiden ominaisuus Pascalin kaltaisilla kielillä on, että ne ovat tyypiltään turvallisia. Toisin sanoen, värimääritteelle ei voida määrittää virheellistä väriä - sen on aina oltava joko PUNAINEN, VIHREÄ tai SININEN. Sitä vastoin, jos värimuuttuja olisi int, voit määrittää sille minkä tahansa kelvollisen kokonaisluvun, vaikka kyseinen numero ei edusta kelvollista väriä.

Tämä artikkeli antaa sinulle mallin luoda lueteltuja vakioita, jotka ovat:

  • Kirjoita turvallinen
  • Tulostettava
  • Tilattu, käytettäväksi hakemistona
  • Yhdistetty eteen- tai taaksepäin silmukointiin
  • Lukuisia

Tulevassa artikkelissa opit kuinka laajentaa lueteltuja vakioita tilasta riippuvan käyttäytymisen toteuttamiseksi.

Miksi et käyttäisi staattisia finaaleja?

Yhteinen mekanismi luetelluille vakioille käyttää staattisia lopullisia int-muuttujia seuraavasti:

 staattinen lopullinen int RED = 0; staattinen lopullinen int VIHREÄ = 1; staattinen lopullinen int SININEN = 2; ... 

Staattiset finaalit ovat hyödyllisiä

Koska ne ovat lopullisia, arvot ovat vakioita ja muuttumattomia. Koska ne ovat staattisia, ne luodaan vain kerran luokalle tai käyttöliittymälle, jossa ne on määritelty, eikä kerran jokaiselle objektille. Ja koska ne ovat kokonaislukumuuttujia, ne voidaan luetella ja käyttää indeksinä.

Voit esimerkiksi kirjoittaa silmukan luodaksesi luettelon asiakkaan suosikkiväreistä:

 for (int i = 0; ...) {if (customerLikesColor (i)) {favoriteColors.add (i); }} 

Voit myös indeksoida taulukon tai vektorin muuttujien avulla saadaksesi arvoon liittyvän arvon. Oletetaan esimerkiksi, että sinulla on lautapeli, jossa on eri värejä kullekin pelaajalle. Oletetaan, että sinulla on bittikartta jokaiselle värikappaleelle ja kutsutulle menetelmälle näyttö() joka kopioi bittikartan nykyiseen sijaintiin. Yksi tapa laittaa pala taululle voi olla jotain tällaista:

PiecePicture redPiece = uusi PiecePicture (PUNAINEN); PiecePicture greenPiece = uusi PiecePicture (VIHREÄ); PiecePicture bluePiece = uusi PiecePicture (SININEN);

void placePiece (int sijainti, int väri) {setPosition (sijainti); if (väri == PUNAINEN) {näyttö (punainen kappale); } else if (väri == VIHREÄ) {display (greenPiece); } else {display (bluePiece); }}

Mutta käyttämällä kokonaislukuarvoja indeksoidaan palasarjaksi, voit yksinkertaistaa koodia seuraavasti:

 PiecePicture [] pala = {uusi PiecePicture (PUNAINEN), uusi PiecePicture (VIHREÄ), uusi PiecePicture (SININEN)}; void placePiece (int sijainti, int väri) {setPosition (sijainti); näyttö (pala [väri]); } 

Staattisten lopullisten kokonaislukujen suurimmat edut ovat mahdollisuus silmukata vakioiden alueelle ja indeksoida taulukkoon tai vektoriin. Ja kun valintojen määrä kasvaa, yksinkertaistamisvaikutus on vielä suurempi.

Staattiset finaalit ovat kuitenkin riskialttiita

Staattisten lopullisten kokonaislukujen käyttämisessä on kuitenkin muutama haitta. Suurin haittapuoli on tyyppiturvallisuuden puute. Kaikkia laskettuja tai luettuja kokonaislukuja voidaan käyttää "väreinä" riippumatta siitä, onko järkevää tehdä niin. Voit silmukoida määriteltyjen vakioiden loppupuolella tai pysähtyä peittämättä niitä kaikkia, mikä voi helposti tapahtua, jos lisäät tai poistat vakion luettelosta, mutta unohdat säätää silmukan hakemistoa.

Esimerkiksi väri-asetussilmukka saattaa lukea näin:

 for (int i = 0; i <= SININEN; i ++) {if (customerLikesColor (i)) {favoriteColors.add (i); }} 

Myöhemmin voit lisätä uuden värin:

 staattinen lopullinen int RED = 0; staattinen lopullinen int VIHREÄ = 1; staattinen lopullinen int SININEN = 2; staattinen lopullinen int MAGENTA = 3; 

Tai voit poistaa yhden:

 staattinen lopullinen int RED = 0; staattinen lopullinen int SININEN = 1; 

Kummassakin tapauksessa ohjelma ei toimi oikein. Jos poistat värin, saat ajonaikaisen virheen, joka kiinnittää huomion ongelmaan. Jos lisäät värin, et saa lainkaan virheitä - ohjelma ei yksinkertaisesti kata kaikkia värivaihtoehtoja.

Toinen haittapuoli on luettavan tunnisteen puuttuminen. Jos käytät nykyisen värivalinnan näyttämiseen viestiruutua tai konsolilähtöä, saat numeron. Se tekee virheenkorjauksesta melko vaikeaa.

Luettavan tunnisteen luomisen ongelmat ratkaistaan ​​joskus staattisilla lopullisilla merkkijonovakioilla, kuten tämä:

 staattinen lopullinen merkkijono PUNAINEN = "punainen". sisäosa (); ... 

Käyttämällä työharjoittelija() menetelmä takaa, että sisäisessä merkkijonopoolissa on vain yksi merkkijono, jolla on kyseinen sisältö. Mutta työharjoittelija() Jotta jokainen merkkijono tai merkkijonomuuttuja, jota koskaan verrataan RED: ään, on käytettävä sitä. Silloinkin staattiset lopulliset merkkijonot eivät salli silmukointia tai indeksointia taulukkoon, eivätkä ne silti käsittele tyyppiturvallisuutta.

Tyypin turvallisuus

Staattisten lopullisten kokonaislukujen ongelmana on, että niitä käyttävät muuttujat ovat luonnostaan ​​rajattomia. Ne ovat int-muuttujia, mikä tarkoittaa, että ne voivat sisältää minkä tahansa kokonaisluvun, ei vain vakioita, joita heidän oli tarkoitus pitää. Tavoitteena on määritellä muuttuja, jonka tyyppi on Väri, jotta saat kokoamisvirheen eikä ajonaikaisen virheen aina, kun tälle muuttujalle annetaan virheellinen arvo.

Tyylikäs ratkaisu tarjottiin Philip Bishopin artikkelissa JavaWorldissa "Typesafe-vakiot C ++: ssa ja Javassa".

Idea on todella yksinkertainen (kun näet sen!):

julkinen finaaliluokka Väri {// finaaliluokka !! yksityinen väri () {} // yksityinen rakentaja !!

julkinen staattinen lopullinen väri PUNAINEN = uusi väri (); julkinen staattinen lopullinen väri VIHREÄ = uusi väri (); julkinen staattinen lopullinen väri SININEN = uusi väri (); }

Koska luokka on määritelty lopulliseksi, sitä ei voida luokitella alaluokkiin. Siitä ei luoda muita luokkia. Koska konstruktori on yksityinen, muut menetelmät eivät voi käyttää luokkaa uusien objektien luomiseen. Ainoat objektit, jotka koskaan luodaan tällä luokalla, ovat staattiset objektit, jotka luova luokka itselleen ensimmäistä kertaa kun luokkaan viitataan! Tämä toteutus on muunnelma Singleton-mallista, joka rajoittaa luokan ennalta määrättyyn esiintymien määrään. Voit käyttää tätä mallia luodaksesi täsmälleen yhden luokan milloin tahansa tarvitset Singletonia, tai käytä sitä tässä esitetyllä tavalla luodaksesi kiinteän määrän esiintymiä. (Singleton-malli on määritelty kirjassa Suunnittelumallit: Uudelleenkäytettävien olio-ohjelmistojen elementit kirjoittanut Gamma, Helm, Johnson ja Vlissides, Addison-Wesley, 1995. Katso linkki tähän kirjaan Resurssit-osiosta.)

Tämän luokkamäärittelyn hämmentävä osa on se, jota luokka käyttää itse luoda uusia esineitä. Kun viittaat PUNAiseen ensimmäistä kertaa, sitä ei ole olemassa. Mutta RED: n määrittelemän luokan käyttäminen aiheuttaa sen luomisen yhdessä muiden vakioiden kanssa. Tosin tällaista rekursiivista viittausta on melko vaikea visualisoida. Mutta etuna on tyyppiturvallisuus. Muuttujalle, jonka tyyppi on Väri, ei voi koskaan määrittää mitään muuta kuin PUNAISET, VIHREÄT tai SINISET objektit, jotka Väri luokka.

Tunnisteet

Ensimmäinen parannus typeafe-luetteloituun vakioluokkaan on luoda vakioista merkkijonoesitys. Haluat pystyä tuottamaan luettavan version arvosta tällä rivillä:

 System.out.println (myColor); 

Aina kun tuot objektin merkkilähtövirtaan, kuten System.out, ja aina kun liität objektin merkkijonoksi, Java kutsuu automaattisesti toString () menetelmä kyseiselle objektille. Se on hyvä syy määritellä a toString () menetelmä jokaiselle luomallesi luokalle.

Jos luokassa ei ole a toString () -menetelmällä perintö hierarkiaa tarkastetaan, kunnes se löytyy. Hierarkian yläosassa toString () menetelmä Esine class palauttaa luokan nimen. Joten toString () menetelmä on aina ollut jonkin verran merkitys, mutta useimmiten oletusmenetelmä ei ole kovin hyödyllinen.

Tässä on muutos Väri luokka, joka tarjoaa hyödyllisen toString () menetelmä:

julkinen finaaliluokka Väri { yksityinen merkkijono id; yksityinen väri (MerkkijonoID) {this.id = anID; } public String toString () {return this.id; }

julkinen staattinen lopullinen väri PUNAINEN = uusi väri (

"Punainen"

); julkinen staattinen lopullinen väri VIHREÄ = uusi väri (

"Vihreä"

); julkinen staattinen lopullinen väri SININEN = uusi väri (

"Sininen"

); }

Tämä versio lisää yksityisen merkkijonomuuttujan (id). Rakentajaa on muokattu ottamaan String-argumentti ja tallentamaan se objektin tunnukseksi. toString () method palauttaa sitten objektin tunnuksen.

Yksi temppu, jota voit käyttää toString () menetelmä hyödyntää sitä, että se käynnistetään automaattisesti, kun objekti liitetään merkkijonoksi. Tämä tarkoittaa, että voit laittaa objektin nimen valintaikkunaan yhdistämällä sen tyhjäksi merkkijonoksi seuraavan rivin avulla:

 textField1.setText ("" + omaväri); 

Ellet satuta rakastamaan kaikkia Lispin sulkeita, huomaat, että ne ovat hieman luettavampia kuin vaihtoehto:

 textField1.setText (myColor.toString ()); 

On myös helpompaa varmistaa, että syötät oikean määrän sulkeita!

Tilaaminen ja indeksointi

Seuraava kysymys on, kuinka indeksoidaan vektoriksi tai taulukoksi käyttämällä

Väri

luokassa. Mekanismina on määrittää järjestysluku jokaiselle luokan vakiolle ja viitata siihen attribuutin avulla

.ord

, kuten tämä:

 void placePiece (int sijainti, int väri) {setPosition (sijainti); näyttö (pala [väri.ord]); } 

Vaikka kiinni .ord muuntaa viittaus väri- luku ei ole erityisen kaunis, se ei myöskään ole kovin häiritsevä. Se näyttää melko kohtuulliselta kompromissilta tyypin kassavakioille.

Näin järjestysnumerot määritetään:

julkinen finaaliluokka Väri {private String id; julkinen lopullinen int ord;yksityinen staattinen int upperBound = 0; yksityinen väri (String anID) {this.id = anID; this.ord = upperBound ++; } public String toString () {return this.id; } julkinen staattinen int-koko () {return upperBound; }

julkinen staattinen lopullinen väri PUNAINEN = uusi väri ("punainen"); julkinen staattinen lopullinen väri VIHREÄ = uusi väri ("vihreä"); julkinen staattinen lopullinen väri SININEN = uusi väri ("sininen"); }

Tämä koodi käyttää uutta JDK-version 1.1 määritelmää "tyhjä lopullinen" muuttuja - muuttuja, jolle annetaan arvo vain kerran. Tämän mekanismin avulla jokaisella objektilla on oma ei-staattinen lopullinen muuttuja, tai, joka osoitetaan kerran objektin luomisen aikana ja joka sen jälkeen pysyy muuttumattomana. Staattinen muuttuja ylempiSidottu seuraa kokoelman seuraavaa käyttämätöntä hakemistoa. Tästä arvosta tulee tai määritettä, kun objekti luodaan, minkä jälkeen ylärajaa lisätään.

Yhteensopivuus Vektori luokka, menetelmä koko() on määritetty palauttamaan tässä luokassa määriteltyjen vakioiden määrä (joka on sama kuin yläraja).

Puristi saattaa päättää, että muuttuja tai tulisi olla yksityinen, ja menetelmä tulisi nimetä ord () pitäisi palauttaa se - jos ei, menetelmä nimeltä getOrd (). Kallistun kohti pääsyä määritteeseen suoraan, kuitenkin kahdesta syystä. Ensimmäinen on, että järjestyskäsite on yksiselitteisesti int. On vain vähän todennäköisyyttä, että toteutus koskaan muuttuisi. Toinen syy on, että mitä todella haluta on kyky käyttää objektia ikään kuin se olisi int, kuten voisit Pascalin kaltaisella kielellä. Voit esimerkiksi käyttää määritettä väri- indeksoida taulukko. Mutta et voi käyttää Java-objektia siihen suoraan. Mitä todella haluat sanoa, on:

 näyttö (pala [väri]); // toivottavaa, mutta ei toimi 

Mutta et voi tehdä sitä. Pienin muutos, joka tarvitaan, jotta saat haluamasi, on käyttää attribuuttia sen sijaan, että tämä on seuraava:

 näyttö (pala [väri.ord]); // lähinnä toivottavaa 

pitkän vaihtoehdon sijasta:

 näyttö (pala [väri.ord ()]); // ylimääräiset sulkeet 

tai vielä pidempi:

 näyttö (pala [väri.getOrd ()]); // ylimääräiset sulkeet ja teksti 

Eiffel-kieli käyttää samaa syntaksia määritteiden käyttämiseen ja menetelmien kutsumiseen. Se olisi ihanteellinen. Ottaen huomioon tarpeen valita yksi tai toinen, olen mennyt pääsyyn tai attribuuttina. Onneksi tunniste tai tulee toistamisen seurauksena niin tutuksi, että sen käyttö tuntuu yhtä luonnolliselta kuin kirjoittaminen int. (Niin luonnollista kuin se saattaa olla.)

Silmukka

Seuraava vaihe on mahdollisuus toistaa luokan vakiot. Haluat pystyä jatkamaan alusta loppuun:

 varten (Väri c = Väri.ensimmäinen (); c! = null; c = c.seuraava ()) {...} 

tai lopusta takaisin alkuun:

 varten (Väri c = Väri.viimeinen (); c! = null; c = c.prev ()) {...} 

Nämä muutokset käyttävät staattisia muuttujia seuraamaan viimeksi luotua objektia ja liittämään sen seuraavaan objektiin: