Programiranje

Ustvarite naštete konstante v Javi

Nabor "neštetih konstant" je urejena zbirka konstant, ki jih je mogoče šteti, na primer številke. Ta lastnost vam omogoča, da jih uporabite kot številke za indeksiranje matrike ali pa jih uporabite kot indeksno spremenljivko v zanki for. V Javi so takšni predmeti najpogosteje znani kot "naštete konstante".

Uporaba naštetih konstant lahko naredi kodo bolj berljivo. Na primer, morda boste želeli določiti nov podatkovni tip z imenom Barva s konstantami RDEČA, ZELENA in MODRA kot možne vrednosti. Zamisel je, da je barva kot atribut drugih predmetov, ki jih ustvarite, na primer avtomobilov:

 razred Avto {Barvna barva; ...} 

Nato lahko napišete jasno, berljivo kodo, kot je ta:

 myCar.color = RDEČA; 

namesto nekaj takega:

 myCar.color = 3; 

Še pomembnejši atribut naštetih konstant v jezikih, kot je Pascal, je, da so tipično varni. Z drugimi besedami, atributu barve ni mogoče dodeliti neveljavne barve - vedno mora biti RDEČA, ZELENA ali MODRA. Če bi bila spremenljivka barve int, bi ji lahko dodelili katero koli veljavno celo število, tudi če to število ne bi predstavljalo veljavne barve.

Ta članek vsebuje predlogo za ustvarjanje naštetih konstant, ki so:

  • Vnesite varno
  • Za tiskanje
  • Naročeno, za uporabo kot indeks
  • Povezani, za zanke naprej ali nazaj
  • Nešteto

V prihodnjem članku boste izvedeli, kako razširiti naštete konstante za izvajanje vedenja, odvisnega od države.

Zakaj ne bi uporabili statičnih finalov?

Skupni mehanizem za oštevilčene konstante uporablja statične končne spremenljivke int, kot je ta:

 statični končni int RDEČI = 0; statični končni int GREEN = 1; statični končni int MODRA = 2; ... 

Statični finali so koristni

Ker so dokončne, so vrednosti konstantne in nespremenljive. Ker so statični, so ustvarjeni samo enkrat za razred ali vmesnik, v katerem so definirani, namesto enkrat za vsak predmet. In ker gre za celoštevilčne spremenljivke, jih je mogoče našteti in uporabiti kot indeks.

Na primer, lahko napišete zanko, da ustvarite seznam najljubših barv stranke:

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

Prav tako lahko indeksirate v matriko ali vektor s pomočjo spremenljivk, da dobite vrednost, povezano z barvo. Recimo, da imate na primer družabno igro, ki ima za vsakega igralca različne barvne koščke. Recimo, da imate bitno sliko za vsak barvni del in imenovano metodo zaslon () ki kopira to bitno sliko na trenutno lokacijo. Eden od načinov, kako kos postaviti na desko, je lahko približno tak:

PiecePicture redPiece = nova PiecePicture (RDEČA); PiecePicture greenPiece = nova PiecePicture (ZELENA); PiecePicture bluePiece = nova PiecePicture (MODRA);

void placePiece (int lokacija, int barva) {setPosition (lokacija); if (color == RDEČA) {display (redPiece); } sicer če (barva == ZELENA) {prikaz (greenPiece); } else {prikaz (bluePiece); }}

Toda z uporabo celoštevilnih vrednosti za indeksiranje v niz kosov lahko poenostavite kodo tako, da:

 PiecePicture [] kos = {nova PiecePicture (RDEČA), nova PiecePicture (ZELENA), nova PiecePicture (MODRA)}; void placePiece (int lokacija, int barva) {setPosition (lokacija); zaslon (kos [barvni]); } 

Glavne prednosti statičnih končnih celih števil so zmožnost zanke po območju konstant in indeksiranja v matriko ali vektor. In ko število možnosti naraste, je učinek poenostavitve še večji.

Toda statični finali so tvegani

Kljub temu obstaja nekaj pomanjkljivosti uporabe statičnih končnih celih števil. Glavna pomanjkljivost je pomanjkanje tipske varnosti. Vsako celo število, ki se izračuna ali prebere, se lahko uporabi kot "barva", ne glede na to, ali je to smiselno. Lahko zavijete tik ob koncu definiranih konstant ali pa preprečite, da bi jih vse zajeli, kar se lahko zgodi, če dodate ali odstranite konstanto s seznama, vendar pozabite prilagoditi indeks zanke.

Na primer, zanka za barvne nastavitve se lahko glasi takole:

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

Pozneje lahko dodate novo barvo:

 statični končni int RDEČI = 0; statični končni int GREEN = 1; statični končni int MODRA = 2; statični končni int MAGENTA = 3; 

Lahko pa ga odstranite:

 statični končni int RDEČI = 0; statični končni int MODRA = 1; 

V obeh primerih program ne bo deloval pravilno. Če odstranite barvo, se prikaže napaka med izvajanjem, ki opozori na težavo. Če dodate barvo, ne bo prišlo do nobene napake - program preprosto ne bo zajel vseh barvnih izbir.

Druga pomanjkljivost je pomanjkanje berljivega identifikatorja. Če za prikaz trenutne izbire barve uporabite okno s sporočilom ali izhodno konzolo, dobite številko. Zaradi tega je odpravljanje napak precej težko.

Težave pri ustvarjanju berljivega identifikatorja se včasih rešijo s statičnimi končnimi konstantami nizov, kot je ta:

 statični končni niz RDEČA = "rdeča" .intern (); ... 

Uporabljati pripravnik() metoda zagotavlja, da je v notranjem področju nizov samo en niz s to vsebino. Ampak za pripravnik() da bo učinkovit, ga mora uporabljati vsak niz ali spremenljivka niza, ki se kdaj primerja z RDEČO. Tudi takrat statični končni nizi ne omogočajo zanke ali indeksiranja v matriko in še vedno ne obravnavajo vprašanja varnosti tipa.

Varnost tipa

Problem statičnih končnih celih števil je v tem, da so spremenljivke, ki jih uporabljajo, same po sebi neomejene. So spremenljivke int, kar pomeni, da lahko vsebujejo katero koli celo število, ne samo konstant, ki so jih nameravali imeti. Cilj je definirati spremenljivko tipa Color, tako da prejmete napako pri prevajanju in ne napako med izvajanjem, kadar je tej spremenljivki dodeljena neveljavna vrednost.

Elegantna rešitev je bila podana v članku Philipa Bishop-a v JavaWorldu, "Konstante Typesafe v C ++ in Java."

Ideja je res preprosta (ko jo enkrat vidite!):

javni končni razred Barva {// končni razred !! private Color () {} // zasebni konstruktor !!

javna statična končna barva RDEČA = nova barva (); javna statična končna barva ZELENA = nova barva (); javna statična končna barva MODRA = nova barva (); }

Ker je razred opredeljen kot končni, ga ni mogoče podrazvrstiti. Iz njega ne bodo ustvarjeni nobeni drugi razredi. Ker je konstruktor zaseben, druge metode ne morejo uporabiti razreda za ustvarjanje novih predmetov. Edini predmeti, ki bodo kdaj ustvarjeni s tem razredom, so statični predmeti, ki jih razred ustvari sam ob prvem sklicevanju na razred! Ta izvedba je različica vzorca Singleton, ki razred omeji na vnaprej določeno število primerkov. Ta vzorec lahko uporabite za ustvarjanje natančno enega razreda, kadar koli potrebujete Singleton, ali pa ga uporabite, kot je prikazano tukaj, da ustvarite določeno število primerkov. (V knjigi je opredeljen vzorec Singleton Vzorci oblikovanja: elementi predmetno usmerjene programske opreme za večkratno uporabo avtor Gamma, Helm, Johnson in Vlissides, Addison-Wesley, 1995. Za povezavo do te knjige glejte razdelek Viri.)

Osupljiv del te definicije razreda je, da razred uporablja sama ustvariti nove predmete. Ko se prvič sklicujete na RDEČO, ne obstaja. Toda dejanje dostopa do razreda, v katerem je definirana RDEČA, povzroči njegovo ustvarjanje skupaj z drugimi konstantami. Resda je takšno rekurzivno sklicevanje precej težko predstaviti. Prednost pa je popolna varnost tipa. Spremenljivki tipa Color ni mogoče nikoli dodeliti ničesar drugega kot RDEČI, ZELENI ali MODRI predmeti, ki jih Barva razred ustvarja.

Identifikatorji

Prva izboljšava v tipu varno naštetega konstantnega razreda je ustvariti nizovno predstavitev konstant. Želite biti sposobni ustvariti berljivo različico vrednosti s tako vrstico:

 System.out.println (myColor); 

Kadar koli izpišete predmet v izhodni tok znakov, kot je System.outin vsakič, ko objekt združite v niz, Java samodejno prikliče datoteko toString () metoda za ta objekt. To je dober razlog za določitev a toString () za kateri koli nov razred, ki ga ustvarite.

Če razred nima toString () metode, se hierarhija dedovanja pregleduje, dokler je ne najdemo. Na vrhu hierarhije je toString () metoda v Predmet class vrne ime razreda. Torej toString () metoda vedno nekaj pomen, vendar največkrat privzeta metoda ne bo zelo uporabna.

Tu je sprememba Barva razred, ki ponuja uporabno toString () metoda:

javni končni razred Color { ID zasebnega niza; zasebna barva (Niz anID) {this.id = anID; } javni String toString () {return this.id; }

javna statična končna barva RDEČA = nova barva (

"Rdeča"

); javna statična končna barva ZELENA = nova barva (

"Zelena"

); javna statična končna barva MODRA = nova barva (

"Modra"

); }

Ta različica doda zasebno spremenljivko String (id). Konstruktor je bil spremenjen tako, da sprejme argument String in ga shrani kot ID predmeta. The toString () nato vrne ID predmeta.

En trik, s katerim lahko prikličete toString () metoda izkorišča dejstvo, da se samodejno pokliče, ko je objekt združen v niz. To pomeni, da lahko ime predmeta vstavite v pogovorno okno tako, da ga povežete v ničelni niz s pomočjo vrstice, kot je naslednja:

 textField1.setText ("" + moja barva); 

Če slučajno ne obožujete vseh oklepajev v Lispu, boste to našli nekoliko bolj berljivo kot druga možnost:

 textField1.setText (myColor.toString ()); 

Prav tako je lažje poskrbeti, da ste vpisali pravo število oklepajev!

Naročanje in indeksiranje

Naslednje vprašanje je, kako indeksirati v vektor ali matriko s pomočjo članov

Barva

razred. Mehanizem bo dodal zaporedno številko vsaki konstanti razreda in jo skliceval z uporabo atributa

.or

, Všečkaj to:

 void placePiece (int lokacija, int barva) {setPosition (lokacija); zaslon (kos [barvni.or]); } 

Čeprav se loteva .or za pretvorbo sklica v barva v številko ni posebej lepa, niti strašno vsiljiva. Zdi se, da je za varne vrste konstant dokaj primeren kompromis.

Tu so podane vrstne številke:

javni končni razred Color {private String id; javni končni red;zasebni statični int upperBound = 0; zasebna barva (niz anID) {this.id = anID; this.ord = upperBound ++; } javni String toString () {return this.id; } public static int size () {return upperBound; }

javna statična končna barva RDEČA = nova barva ("rdeča"); javna statična končna barva ZELENA = nova barva ("zelena"); javna statična končna barva MODRA = nova barva ("Modra"); }

Ta koda uporablja novo definicijo JDK različice 1.1 spremenljivke "prazen končni" - spremenljivke, ki ji je dodeljena vrednost enkrat in samo enkrat. Ta mehanizem omogoča vsakemu objektu lastno končno spremenljivko, ki ni statična, red, ki bo dodeljen enkrat med ustvarjanjem predmeta in ki bo nato ostal nespremenljiv. Statična spremenljivka Zgornja meja beleži naslednji neuporabljeni indeks v zbirki. Ta vrednost postane red atribut, ko je objekt ustvarjen, po katerem se zgornja meja poveča.

Za združljivost z Vektor razred, metoda velikost () je definiran tako, da vrne število konstant, ki so bile definirane v tem razredu (kar je enako zgornji meji).

Purist se lahko odloči, da je spremenljivka red mora biti zasebna, metoda pa imenovana ord () bi ga moral vrniti - če ne, metoda z imenom getOrd (). Vendar se nagibam k neposrednemu dostopu do atributa iz dveh razlogov. Prvi je, da je pojem ordinal nedvoumno koncept int. Malo je verjetnosti, če sploh, da bi se izvedba kdaj spremenila. Drugi razlog je to, kar v resnici ste želim je sposobnost uporabe predmeta, kot da bi bil int, kot bi lahko v jeziku, kot je Pascal. Na primer, morda boste želeli uporabiti atribut barva za indeksiranje matrike. Vendar za to ne morete uporabiti predmeta Java. Kaj bi res rad povedal, je:

 zaslon (kos [barvni]); // zaželeno, vendar ne deluje 

Toda tega ne moreš storiti. Najmanjša sprememba, ki je potrebna za pridobitev želenega, je dostop do atributa, namesto tega:

 zaslon (kos [color.ord]); // najbližje zaželenemu 

namesto dolgotrajne alternative:

 zaslon (kos [color.ord ()]); // dodatni oklepaji 

ali še daljši:

 zaslon (kos [color.getOrd ()]); // dodatni oklepaji in besedilo 

Eifflov jezik uporablja isto sintakso za dostop do atributov in metode klicanja. To bi bilo idealno. Glede na potrebo po izbiri enega ali drugega pa sem šel z dostopom red kot atribut. Z vso srečo identifikator red se bodo zaradi ponavljanja tako spoznali, da se bo zdelo tako naravno kot pisanje int. (Čim bolj naravno.)

Looping

Naslednji korak je ponovitev konstante razreda. Želite imeti možnost zanke od začetka do konca:

 za (Barva c = Color.first (); c! = null; c = c.next ()) {...} 

ali od konca nazaj do začetka:

 za (Barva c = Color.last (); c! = null; c = c.prev ()) {...} 

Te spremembe uporabljajo statične spremenljivke za sledenje zadnje ustvarjenemu objektu in njegovo povezavo z naslednjim objektom:

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