Programavimas

Kurkite išvardytas konstantas „Java“

„Suskaičiuojamų konstantų“ rinkinys yra sutvarkytas konstantų rinkinys, kurį galima suskaičiuoti, pavyzdžiui, skaičius. Ta ypatybė leidžia juos naudoti kaip skaičius, norint indeksuoti masyvą, arba galite naudoti kaip indekso kintamąjį for for loop. „Java“ tokie objektai dažniausiai žinomi kaip „išvardytos konstantos“.

Naudojant išvardytas konstantas, kodas gali būti lengviau įskaitomas. Pavyzdžiui, galbūt norėsite kaip naują reikšmę apibrėžti naują duomenų tipą, pavadintą Spalva su RED, GREEN ir BLUE konstantomis. Idėja yra turėti spalvą kaip kitų jūsų sukurtų objektų, tokių kaip automobilių objektai, atributą:

 klasės automobilis {Spalvos spalva; ...} 

Tada galite parašyti aiškų, skaitomą kodą, pavyzdžiui:

 myCar.color = RED; 

vietoj kažko panašaus:

 „myCar.color“ = 3; 

Dar svarbesnis išvardytų konstantų atributas tokiose kalbose kaip „Pascal“ yra tas, kad jos yra saugios. Kitaip tariant, neįmanoma spalvų atributui priskirti netinkamos spalvos - ji visada turi būti RED, GREEN arba BLUE. Priešingai, jei spalvų kintamasis būtų int, tuomet galėtumėte jam priskirti bet kurį galiojantį sveikąjį skaičių, net jei šis skaičius neatitiko tinkamos spalvos.

Šiame straipsnyje pateikiamas šablonas, skirtas sukurti išvardytas konstantas, kurios yra:

  • Tipas seifas
  • Spausdinti
  • Užsakyta, skirta naudoti kaip rodyklė
  • Susieta, skirta kilpai į priekį arba atgal
  • Suskaičiuojama

Iš būsimo straipsnio sužinosite, kaip išplėsti išvardytas konstantas, kad būtų įgyvendintas nuo valstybės priklausomas elgesys.

Kodėl nepasinaudojus statiniais finalais?

Bendras išvardytų konstantų mechanizmas naudoja tokius statinius galutinius kintamuosius:

 statinis galutinis int RED = 0; statinis galutinis int ŽALI = 1; statinis galutinis int MĖLYNA = 2; ... 

Statiniai finalai yra naudingi

Kadangi jos yra galutinės, vertės yra pastovios ir nekeičiamos. Kadangi jie yra statiški, jie kuriami tik vieną kartą klasei ar sąsajai, kurioje jie apibrėžti, o ne vieną kartą kiekvienam objektui. Kadangi jie yra sveikieji skaičiai, juos galima išvardyti ir naudoti kaip indeksą.

Pavyzdžiui, galite parašyti kilpą, kad sukurtumėte mėgstamiausių klientų spalvų sąrašą:

 for (int i = 0; ...) {if (klientas mėgsta spalvą (i)) {mėgstamiausias spalvas.add (i); }} 

Taip pat galite indeksuoti į masyvą ar vektorių naudodami kintamuosius, kad gautumėte su spalva susietą vertę. Pvz., Tarkime, kad turite stalo žaidimą, kuriame kiekvienam žaidėjui skirtingos spalvos gabalai. Tarkime, kad turite kiekvienos spalvos kūrinio bitplaną ir vadinamąjį metodą rodyti () kad nukopijuos tą bitų žemėlapį į dabartinę vietą. Vienas iš būdų įdėti gabalą ant lentos gali būti maždaug toks:

PiecePicture redPiece = nauja PiecePicture (RED); PiecePicture greenPiece = nauja PiecePicture (ŽALIA); PiecePicture bluePiece = nauja PiecePicture (MĖLYNA);

void placePiece (int vieta, int spalva) {setPosition (vieta); if (spalva == RAUDONA) {ekranas (raudonas gabalas); } else if (spalva == ŽALI) {display (greenPiece); } else {display (bluePiece); }}

Tačiau naudodami sveikojo skaičiaus reikšmes indeksuodami į masyvą, galite supaprastinti kodą:

 PiecePicture [] piece = {nauja PiecePicture (RED), nauja PiecePicture (GREEN), nauja PiecePicture (BLUE)}; void placePiece (int vieta, int spalva) {setPosition (vieta); ekranas (gabalas [spalva]); } 

Galimybė susikurti konstantų diapazoną ir indeksuoti į masyvą ar vektorių yra pagrindiniai statinių galutinių skaičių pranašumai. O kai pasirinkimų skaičius auga, supaprastinimo efektas yra dar didesnis.

Tačiau statiniai finalai yra rizikingi

Vis dėlto yra keli trūkumai naudojant statinius galutinius skaičius. Pagrindinis trūkumas yra tipo saugumo trūkumas. Bet koks apskaičiuotas ar perskaitytas skaičius gali būti naudojamas kaip „spalva“, neatsižvelgiant į tai, ar tai prasminga. Galite pereiti tiesiai per apibrėžtų konstantų pabaigą arba nustoti jas visas aprėpti, o tai gali lengvai nutikti, jei pridėsite ar pašalinsite konstantą iš sąrašo, bet pamiršite pakoreguoti kilpos indeksą.

Pvz., Jūsų spalvos parinkties kilpa gali būti tokia:

 už (int i = 0; i <= MĖLYNA; i ++) {if (customerLikesColor (i)) {favoriteColors.add (i); }} 

Vėliau galite pridėti naują spalvą:

 statinis galutinis int RED = 0; statinis galutinis int ŽALI = 1; statinis galutinis int MĖLYNA = 2; statinis galutinis int MAGENTA = 3; 

Arba galite pašalinti vieną:

 statinis galutinis int RED = 0; statinis galutinis int MĖLYNA = 1; 

Bet kuriuo atveju programa neveiks tinkamai. Pašalinę spalvą gausite vykdymo laiko klaidą, kuri atkreipia dėmesį į problemą. Jei pridėsite spalvą, iš viso negausite klaidos - programa paprasčiausiai nepadarys visų spalvų pasirinkimų.

Kitas trūkumas yra skaitomo identifikatoriaus trūkumas. Jei naudojate pranešimų laukelį arba konsolės išvestį dabartiniam spalvų pasirinkimui rodyti, gausite skaičių. Tai apsunkina derinimą.

Problemos, susijusios su skaitomo identifikatoriaus kūrimu, kartais išsprendžiamos naudojant statines galutines eilutės konstantas, tokias:

 statinė galutinė eilutė RED = "raudona". vidinė (); ... 

Naudojant praktikantas () metodas garantuoja, kad vidiniame eilučių telkinyje yra tik viena eilutė su tuo turiniu. Bet už praktikantas () Kad būtų veiksminga, kiekviena eilutė ar eilutės kintamasis, kuris kada nors lyginamas su RED, turi ją naudoti. Net ir tada statinės galutinės eilutės neleidžia kurti ar indeksuoti masyvo, ir vis tiek nesprendžia tipo saugos klausimo.

Tipo sauga

Statiškų sveikųjų skaičių problema yra ta, kad juos naudojantys kintamieji yra iš esmės neriboti. Jie yra int kintamieji, o tai reiškia, kad jie gali turėti bet kokį sveiką skaičių, ne tik konstantas, kurias jie turėjo laikyti. Tikslas yra apibrėžti Spalvos tipo kintamąjį, kad gautumėte kompiliavimo klaidą, o ne vykdymo klaidą, kai tam kintamajam priskiriama neteisinga reikšmė.

Elegantiškas sprendimas buvo pateiktas Philipo Bishopo straipsnyje „JavaWorld“ „Typesafe konstantas C ++ ir Java“.

Idėja yra tikrai paprasta (kai tik pamatysite!):

public final class Spalva {// final class !! privatus spalva () {} // privatus konstruktorius !!

public static final Spalva RED = nauja spalva (); public static final Spalva ŽALIA = nauja Spalva (); public static final Spalva MĖLYNA = nauja spalva (); }

Kadangi klasė apibrėžiama kaip galutinė, jos negalima skirstyti. Iš jos nebus kuriamos jokios kitos klasės. Kadangi konstruktorius yra privatus, kiti metodai negali naudoti klasės kurti naujiems objektams. Vieninteliai objektai, kurie kada nors bus sukurti naudojant šią klasę, yra statiniai objektai, kuriuos klasė sukuria sau pirmą kartą nurodžius klasę! Šis diegimas yra „Singleton“ modelio variacija, ribojanti klasę iš anksto nustatytu egzempliorių skaičiumi. Galite naudoti šį modelį kurdami tiksliai vieną klasę bet kada, kai jums reikia „Singleton“, arba naudokite jį, kaip parodyta čia, kad sukurtumėte fiksuotą egzempliorių skaičių. (Singletono modelis aprašytas knygoje Dizaino modeliai: daugkartinio naudojimo objektų programinės įrangos elementai Gamma, Helm, Johnson ir Vlissides, Addison-Wesley, 1995. Nuorodą į šią knygą ieškokite skyriuje „Ištekliai“.)

Neįtikėtina šio klasės apibrėžimo dalis yra ta, kurią klasė naudoja pats kurti naujus objektus. Pirmą kartą nurodant RED, jo nėra. Bet prieiga prie klasės, kurioje RED yra apibrėžta, sukelia ją kartu su kitomis konstantomis. Tiesa, tokią rekursinę nuorodą yra gana sunku įsivaizduoti. Bet privalumas yra visiškas tipo saugumas. Spalvos tipo kintamajam niekada negalima priskirti nieko, išskyrus RED, GREEN ar BLUE objektus, kurie yra Spalva klasė kuria.

Identifikatoriai

Pirmasis „typeafe“ išvardytos konstantos klasės patobulinimas yra sukurti konstantų eilutę. Norite, kad būtų galima sukurti skaitomą vertės versiją su tokia eilute:

 System.out.println („myColor“); 

Kai išvedate objektą į simbolių išvesties srautą, pavyzdžiui, System.outir kiekvieną kartą, kai susiejate objektą su eilute, „Java“ automatiškai iškviečia toString () metodas tam objektui. Tai yra tinkama priežastis apibrėžti toString () metodas bet kuriai naujai kuriamai klasei.

Jei klasėje nėra a toString () metodas, paveldėjimo hierarchija tikrinama tol, kol bus nustatyta. Hierarchijos viršuje toString () metodas Objektas klasė grąžina klasės pavadinimą. Taigi toString () metodas visada kai kurie reikšmę, tačiau dažniausiai numatytasis metodas nebus labai naudingas.

Čia yra modifikacija Spalva klasė, kuri teikia naudingą toString () metodas:

viešosios finalinės klasės spalva { privačios stygos ID; privati ​​spalva (Styginių anID) {this.id = anID; } public String toString () {return this.id; }

public static final spalva RED = nauja spalva (

„Raudona“

); public static final spalva ŽALIA = nauja spalva (

"Žalias"

); public static final spalva MĖLYNA = nauja spalva (

„Mėlyna“

); }

Ši versija prideda privatų eilutės kintamąjį (id). Konstruktorius buvo modifikuotas, kad gautų argumentą „String“ ir išsaugotų jį kaip objekto ID. toString () metodas grąžina objekto ID.

Vienas triukas, kuriuo galite pasinaudoti toString () metodas naudojasi tuo, kad jis automatiškai iškviečiamas, kai objektas sujungiamas į eilutę. Tai reiškia, kad objekto pavadinimą galite įtraukti į dialogą, susiejant jį su nuline eilute, naudojant tokią eilutę:

 textField1.setText ("" + myColor); 

Jei nenorite pamilti visų „Lisp“ skliaustų, suprasite, kad jie yra šiek tiek aiškesni nei alternatyva:

 textField1.setText (myColor.toString ()); 

Taip pat lengviau įsitikinti, kad įrašėte reikiamą skaičių skliaustų!

Užsakymas ir indeksavimas

Kitas klausimas yra kaip indeksuoti į vektorių ar masyvą naudojant

Spalva

klasė. Mechanizmas bus kiekvienos klasės konstantos priskyrimas eilės numeriui ir nuoroda į jį naudojant atributą

.ord

, kaip šitas:

 void placePiece (int vieta, int spalva) {setPosition (vieta); ekranas (gabalas [spalva.ord]); } 

Nors ir laikosi .ord konvertuoti nuorodą į spalva į skaičių nėra ypač gražus, jis taip pat nėra labai įkyrus. Atrodo, kad tai yra gana pagrįstas kompromisas dėl tipinių seifų konstantų.

Štai kaip priskiriami eiliniai skaičiai:

public final class Spalva {private String id; public final int ord;private static int upperBound = 0; privati ​​spalva (String anID) {this.id = anID; this.ord = upperBound ++; } public String toString () {return this.id; } public static int size () {return upperBound; }

public static final Spalva RED = nauja spalva ("Raudona"); public static final Spalva ŽALIA = nauja spalva ("Žalia"); public static final spalva MĖLYNA = nauja spalva („mėlyna“); }

Šiame kode naudojamas naujas JDK 1.1 versijos „tuščio galutinio“ kintamojo apibrėžimas - kintamasis, kuriam reikšmė priskiriama tik vieną kartą. Šis mechanizmas leidžia kiekvienam objektui turėti savo nestatišką galutinį kintamąjį, ord, kuri bus paskirta vieną kartą objekto kūrimo metu ir kuri vėliau išliks nekintama. Statinis kintamasis viršutinėSusieta seka kitą nepanaudotą kolekcijos indeksą. Ta vertybė tampa ord atributas, kai objektas sukuriamas, o po to didinama viršutinė riba.

Dėl suderinamumo su Vektorius klasė, metodas dydis () yra apibrėžtas, kad būtų grąžintas šioje klasėje apibrėžtų konstantų skaičius (kuris yra toks pat kaip viršutinė riba).

Puristas gali nuspręsti, kad kintamasis ord turėtų būti privatus, o metodas įvardytas ord () turėtų jį grąžinti - jei ne, pavadintas metodas getOrd (). Aš linkstu prieiti prie atributo tiesiogiai, nors dėl dviejų priežasčių. Pirmoji yra ta, kad eilės sąvoka vienareikšmiškai yra int. Tikimybė, kad įgyvendinimas kada nors pasikeis, yra maža. Antroji priežastis yra tai, ką tu iš tikrųjų nori yra galimybė naudoti objektą taip, tarsi jis būtų tarpt, kaip jūs galėtumėte tokia kalba kaip Pascal. Pavyzdžiui, galbūt norėsite naudoti atributą spalva indeksuoti masyvą. Bet jūs negalite naudoti „Java“ objekto tam tiesiogiai. Iš tikrųjų norėtumėte pasakyti:

 ekranas (gabalas [spalva]); // pageidautina, bet neveikia 

Bet tu negali to padaryti. Minimalus pakeitimas, reikalingas norint gauti tai, ko norite, yra prieiga prie atributo, o taip:

 ekranas (gabalas [spalva.ord]); // arčiausiai pageidaujamo 

vietoj ilgos alternatyvos:

 ekranas (gabalas [spalva.ord ()]); // papildomi skliaustai 

ar dar ilgesnis:

 ekranas (gabalas [spalva.getOrd ()]); // papildomi skliaustai ir tekstas 

Eifelio kalba naudoja tą pačią sintaksę prieigai prie atributų ir metodų iškvietimo. Tai būtų idealas. Atsižvelgdamas į būtinybę pasirinkti vieną ar kitą, vis dėlto norėjau prisijungti ord kaip atributas. Su bet kokia sėkme, identifikatorius ord kartojimo rezultatas taps toks įprastas, kad jo naudojimas atrodys toks pat natūralus kaip rašymas tarpt. (Kad ir kaip būtų natūralu.)

Kilpa

Kitas žingsnis - galimybė kartoti klasės konstantas. Norite, kad būtų galima pereiti nuo pradžios iki pabaigos:

 už (Spalva c = Spalva.pirma (); c! = Nulis; c = c. Kitas ()) {...} 

arba nuo pabaigos iki pradžios:

 už (Spalva c = Spalva.paskutinė (); c! = nulinė; c = c.prev ()) {...} 

Šie pakeitimai naudoja statinius kintamuosius, kad galėtų sekti paskutinį sukurtą objektą ir susieti jį su kitu objektu: