Programavimas

Kaip naudotis „Java“ rūšies seifais

„Java“ kodas, kuriame naudojami tradiciniai išvardyti tipai, yra problemiškas. „Java 5“ suteikė mums geresnę alternatyvą tipiškų seifų forma. Šiame straipsnyje aš supažindinu jus su išvardintais tipais ir „typeafe enums“, parodau, kaip paskelbti „typeafe enum“ ir naudoti jį jungiklio sakinyje, bei aptariu „Typafe enum“ pritaikymą pridedant duomenis ir elgesį. Straipsnį baigiu tyrinėdamas java.lang.Enum klasė.

atsisiųsti Gauti kodą Atsisiųskite šios „Java 101“ mokymo programos pavyzdžių šaltinio kodą. Sukūrė Jeffas Friesenas, skirtas „JavaWorld“ /.

Nuo išvardytų tipų iki tipiškų seifų

An išvardytas tipas kaip reikšmes nurodo susijusių konstantų rinkinį. Pavyzdžiui, savaitės dienos, standartinės šiaurės / pietų / rytų / vakarų kompaso kryptys, valiutos monetų nominalai ir leksinio analizatoriaus žetonų tipai.

Surašyti tipai tradiciškai buvo įgyvendinami kaip sveikųjų skaičių konstantų sekos, o tai rodo šis krypties konstantų rinkinys:

statinis galutinis int DIR_NORTH = 0; statinis galutinis int DIR_WEST = 1; statinis galutinis int DIR_EAST = 2; statinis galutinis int DIR_SOUTH = 3;

Yra keletas šio požiūrio problemų:

  • Tipo saugos trūkumas: Kadangi išvardyta tipo konstanta yra tik sveikasis skaičius, bet kurį sveiką skaičių galima nurodyti ten, kur reikalinga konstanta. Be to, šioms konstantoms galima atlikti susiejimo, atimties ir kitas matematines operacijas; pavyzdžiui, (DIR_NORTH + DIR_EAST) / DIR_SOUTH), kuris yra beprasmis.
  • Vardų srities nėra: Surašyto tipo konstantos turi būti pridedamos su tam tikru (tikiuosi) unikaliu identifikatoriumi (pvz., DIR_), kad būtų išvengta susidūrimų su kito išvardinto tipo konstantomis.
  • Trapumas: Kadangi išvardytos tipo konstantos yra sudaromos į klasės failus, kur jų tiesioginės reikšmės saugomos (pastoviuose telkiniuose), norint pakeisti konstantos vertę, reikia atstatyti šias klasės ir nuo jų priklausančias programų klasės bylas. Priešingu atveju neapibrėžtas elgesys bus vykdomas.
  • Informacijos stoka: Kai atspausdinama konstanta, išvedama jos sveiko skaičiaus reikšmė. Šis išvestis nieko nepasako apie tai, ką reiškia sveikojo skaičiaus reikšmė. Jis net nenustato išvardinto tipo, kuriam priklauso konstanta.

Naudodamiesi galite išvengti „tipo saugumo trūkumo“ ir „informacijos trūkumo“ problemų java.lang.Stringas konstantos. Pavyzdžiui, galite nurodyti statinė galutinė eilutė DIR_NORTH = "NORTH";. Nors pastovi vertė yra prasmingesnė, Stygoskonstantos vis dar kenčia nuo „vardų srities nėra“ ir trapumo problemų. Be to, skirtingai nei sveikųjų skaičių palyginimai, negalima lyginti eilutės reikšmių su == ir != operatoriai (kurie lygina tik nuorodas).

Dėl šių problemų kūrėjai sugalvojo klasių alternatyvą, vadinamą „Typesafe Enum“. Šis modelis buvo plačiai aprašytas ir kritikuojamas. Joshua Blochas pristatė modelį savo 21 punkte Efektyvus „Java“ programavimo kalbos vadovas (Addison-Wesley, 2001) ir pažymėjo, kad jis turi tam tikrų problemų; būtent tai, kad nepatogu sugrupuoti typeafe enum konstantas į rinkinius ir kad surašymo konstantos negali būti naudojamos perjungti pareiškimus.

Apsvarstykite šį „typeafe enum“ modelio pavyzdį. Kostiumas klasė parodo, kaip galite naudoti klasės alternatyvą įvesti išvardytą tipą, apibūdinantį keturis kortų kostiumus (klubus, deimantus, širdis ir kastuvus):

viešosios finalinės klasės kostiumas // Neturėtų sugebėti subklasifikuoti „Suit“. {public static final Suit CLUBS = naujas kostiumas (); public static final Suit DIAMONDS = naujas kostiumas (); public static final Kostiumas ŠIRDYS = new Kostiumas (); public static final Suit SPADES = naujas kostiumas (); private Suit () {} // Neturėtų būti įmanoma įvesti papildomų konstantų. }

Norėdami naudotis šia klase, pristatytumėte a Kostiumas kintamąjį ir priskirkite jį vienam iš KostiumasKonstantos:

Kostiumo kostiumas = kostiumas.DIAMONDS;

Tada galbūt norėsite tardyti kostiumas a perjungti toks teiginys:

jungiklis (kostiumas) {case Suit.CLUBS: System.out.println ("klubai"); pertrauka; dėklas Suit.DIAMONDS: System.out.println („deimantai“); pertrauka; byla Kostiumas.HEARTS: System.out.println ("širdys"); pertrauka; byla Suit.SPADES: System.out.println ("kastuvai"); }

Tačiau kai susiduria „Java“ kompiliatorius Kostiumas. KLUBAI, ji praneša apie klaidą, teigdama, kad reikalinga pastovi išraiška. Galite pabandyti išspręsti problemą taip:

jungiklis (kostiumas) {atvejis CLUBS: System.out.println ("klubai"); pertrauka; dėklas DIAMONDS: System.out.println („deimantai“); pertrauka; atvejis ŠIRDYS: System.out.println ("širdys"); pertrauka; dėklas SPADES: System.out.println ("kastuvai"); }

Tačiau susidūrus kompiliatoriui KLUBAI, ji praneš apie klaidą nurodydama, kad nepavyko rasti simbolio. Ir net jei padėjote Kostiumas pakete, importavo paketą ir statiškai importavo šias konstantas, kompiliatorius skundėsi, kad negali konvertuoti Kostiumas į tarpt kai susiduri kostiumas į jungiklis (kostiumas). Dėl kiekvieno atveju, kompiliatorius taip pat praneša, kad reikalinga pastovi išraiška.

„Java“ nepalaiko „Typesafe Enum“ modelio perjungti pareiškimus. Tačiau ji pristatė typesafe enum kalbos funkcija, skirta apibendrinti modelio pranašumus sprendžiant jo problemas, ir ši funkcija palaiko perjungti.

„Typafe enum“ deklaravimas ir naudojimas jungikliu

Paprasta „Java“ kodo „typeafe enum“ deklaracija atrodo kaip jos atitikmenys C, C ++ ir C # kalbomis:

enum Kryptis {NORTH, WEST, EAST, SOUTH}

Šioje deklaracijoje naudojamas raktinis žodis enum pristatyti Kryptis kaip „typeafe enum“ (speciali klasės rūšis), į kurį galima įtraukti savavališkus metodus ir įgyvendinti savavališkas sąsajas. ŠIAURĖ, VAKARAI, RYTAIir PIETŲenum konstantos yra įgyvendinamos kaip pastoviai specifinės klasės įstaigos, apibrėžiančios anonimines klases, pratęsiančias uždarą Kryptis klasė.

Kryptis ir kitų tipų seifai Enum ir paveldi įvairius metodus, įskaitant vertės (), toString ()ir palyginti su(), iš šios klasės. Mes ištirsime Enum vėliau šiame straipsnyje.

1 sąrašas skelbia minėtą enumą ir naudoja jį a perjungti pareiškimas. Tai taip pat parodo, kaip palyginti dvi enumo konstantas, nustatyti, kuri konstanta eina prieš kitą konstantą.

1 sąrašas: TEDemo.java (1 versija)

public class TEDemo {enum Direction {NORTH, WEST, EAST, SOUTH} public static void main (String [] args) {for (int i = 0; i <Direction.values ​​(). length; i ++) {Direction d = Direction .values ​​() [i]; System.out.println (d); jungiklis (d) {atvejis NORTH: System.out.println ("Judėti į šiaurę"); pertrauka; atvejis WEST: System.out.println („Perkelti į vakarus“); pertrauka; atvejis EAST: System.out.println ("Judėti į rytus"); pertrauka; atvejis PIETAI: System.out.println ("Judėk į pietus"); pertrauka; numatytasis: teigti klaidingą: "nežinoma kryptis"; }} System.out.println (Kryptis.NORTH.compareTo (Kryptis. PIETAI)); }}

1 sąrašas skelbia Kryptis typesafe enum ir kartoja savo nuolatinius narius, kurie vertės () grįžta. Kiekvienos vertės atveju perjungti pareiškime (patobulintame, kad būtų palaikomi tipiniai seifai) pasirenkamas atveju kad atitinkad ir išleidžia atitinkamą pranešimą. (Jūs nenurodote priešskaitos konstantos, pvz., ŠIAURĖ, su savo enum tipu.) Galiausiai įvertinamas 1 sąrašas Direction.NORTH.compareTo (Direction.SOUTH) nustatyti, ar ŠIAURĖ ateina anksčiau PIETŲ.

Sudarykite šaltinio kodą taip:

javac TEDemo.java

Paleiskite sukompiliuotą programą taip:

java TEDemo

Turėtumėte stebėti šį rezultatą:

ŠIAURĖ Perkelti į šiaurę Vakarai Perkelti į vakarus RYTAI Judėti į rytus PIETAI Perkelti į pietus -3

Rezultatas rodo, kad paveldėta toString () metodas grąžina enumo konstantos pavadinimą ir tas ŠIAURĖ ateina anksčiau PIETŲ palyginus šias enum konstantas.

Duomenų ir elgsenos įtraukimas į „typeafe enum“

Duomenis (laukų pavidalu) ir elgseną (metodų pavidalu) galite pridėti prie typeafe enum. Pavyzdžiui, tarkime, kad jums reikia įvesti Kanados monetų sąrašą ir kad ši klasė turi suteikti priemonių grąžinti nikelių, dimetų, ketvirčių ar dolerių skaičių, esantį savavališkame centų skaičiuje. 2 sąrašas rodo, kaip atlikti šią užduotį.

2 sąrašas: TEDemo.java (2 versija)

enum Moneta {NICKEL (5), // konstantos pirmiausia turi pasirodyti DIME (10), QUARTER (25), DOLLAR (100); // kabliataškis yra būtinas privatus galutinis int valueInPennies; Moneta (int valueInPennies) {this.valueInPennies = valueInPennies; } int toCoins (int pennies) {grąžinti centus / valueInPennies; }} public class TEDemo {public static void main (String [] args) {if (args.length! = 1) {System.err.println ("naudojimas: java TEDemo sumaInPennies"); grįžti; } int centai = Sveikasis skaičius.parseInt (argumentai [0]); for (int i = 0; i <Monetos vertės (). ilgis; i ++) System.out.println (centai + "centuose yra" + monetos.values ​​() [i] .toCoins (centai) + "" + moneta .values ​​() [i] .toString (). toLowerCase () + "s"); }}

2 sąraše pirmiausia deklaruojama a Moneta enum. Parametruotų konstantų sąraše nurodomos keturios monetų rūšys. Kiekvienai konstantai pateiktas argumentas atspindi monetų atstovaujamų centų skaičių.

Kiekvienai konstantai perduotas argumentas iš tikrųjų perduodamas Moneta (int valueInPennies) konstruktorius, kuris išsaugo argumentą vertybės egzemplioriaus laukas. Šis kintamasis pasiekiamas iš monetos () egzemplioriaus metodas. Jis padalijamas į perduotų centų skaičių toCoin ()’S centų parametras, ir šis metodas pateikia rezultatą, kuris yra monetų skaičius piniginiu nominalu, aprašytas Moneta pastovus.

Šiuo metu jūs pastebėjote, kad egzempliorių laukus, konstruktorius ir egzempliorių metodus galite deklaruoti tipiniame leidinyje. Galų gale „typeafe enum“ iš esmės yra speciali „Java“ klasės rūšis.

TEDemo klasės pagrindinis () metodas pirmiausia patikrina, ar nurodytas vienas komandinės eilutės argumentas. Šis argumentas paverčiamas sveikuoju skaičiumi iškviečiant java.lang.Integer klasės parseInt () metodas, kuris analizuoja eilutės argumento vertę į sveikąjį skaičių (arba išmeta išimtį, kai aptinkama neteisinga įvestis). Turėsiu daugiau pasakyti Sveikasis skaičius ir jo pusbrolio klasės ateityje „Java 101“ straipsnis.

Judeti i prieki, pagrindinis () pasikartoja Moneta’Konstantos. Kadangi šios konstantos yra saugomos a Moneta [] masyvas, pagrindinis () vertina Moneta.values ​​(). Ilgis nustatyti šio masyvo ilgį. Kiekvienai kilpos indekso iteracijai i, pagrindinis () vertina Monetų vertės () [i] prieiti prie Moneta pastovus. Tai kviečia kiekvieną monetos () ir toString () dėl šios konstantos, kuri tai dar labiau įrodo Moneta yra ypatinga klasės rūšis.

Sudarykite šaltinio kodą taip:

javac TEDemo.java

Paleiskite sukompiliuotą programą taip:

java TEDemo 198

Turėtumėte stebėti šį rezultatą:

198 centuose yra 39 nikeliai. 198 centuose yra 19 dimes. 198 centuose yra 7 ketvirčiai. 198 centuose yra 1 doleris

Naršyti Enum klasė

„Java“ kompiliatorius svarsto enum kad būtų sintaksinis cukrus. Susidūręs su typeafe enum deklaracija, ji sukuria klasę, kurios pavadinimas nurodytas deklaracijoje. Ši klasė klasifikuoja abstraktą Enum klasė, kuri yra visų tipų seifų pagrindinė klasė.

EnumOficialus tipo parametrų sąrašas atrodo siaubingai, bet tai nėra taip sunku suprasti. Pavyzdžiui, kontekste Moneta pratęsia Enumą, šį oficialų tipo parametrų sąrašą interpretuotumėte taip:

  • Bet koks poklasis Enum turi pateikti faktinio tipo argumentą Enum. Pavyzdžiui, Moneta’Antraštė nurodo Enum.
  • Tikrasis tipo argumentas turi būti poklasis Enum. Pavyzdžiui, Moneta yra poklasis Enum.
  • Subklasė Enum (toks kaip Moneta) turi vadovautis idėja, kad ji pateikia savo vardą (Moneta) kaip faktinis tipo argumentas.

Nagrinėk Enum„Java“ dokumentaciją ir pastebėsite, kad ji nepaisoma java.lang.Object's klonas (), lygi (), baigti (), hashCode ()ir toString () metodai. Išskyrus toString (), deklaruojami visi šie svarbesni metodai galutinis kad jų nebūtų galima pakeisti poklasyje:

  • klonas () yra nepaisoma, kad būtų išvengta konstantų klonavimo, kad niekada nebūtų daugiau nei vienos konstantos kopijos; priešingu atveju konstantų nebuvo galima palyginti == ir !=.
  • lygi () yra ignoruojamas, norint palyginti konstantas per jų nuorodas. Pastovios tapatybės (==) turi būti tas pats turinys (lygi ()), o skirtingos tapatybės reiškia skirtingą turinį.
  • baigti () yra nepaisoma, siekiant užtikrinti, kad konstantos negalėtų būti baigtos.
  • hashCode () yra nepaisoma, nes lygi () yra nepaisoma.
  • toString () yra nepaisoma, kad būtų grąžintas konstantos vardas.

Enum taip pat pateikia savo metodus. Šie metodai apima galutinispalyginti su() (Enum įgyvendina java.lang.Palyginamas sąsaja), „getDeclaringClass“ (), vardas()ir eilinis () metodai: