Programiranje

Za primerjavo Java Enumov uporabite == (ali! =)

Večina novih razvijalcev Java se hitro nauči, da bi morali na splošno primerjati nize Java z uporabo String.equals (Object) in ne z uporabo ==. To je novim razvijalcem večkrat poudarjeno in poudarjeno, ker so skoraj vedno pomeni primerjati vsebino niza (dejanski znaki, ki tvorijo niz) in ne identiteto niza (njegov naslov v spominu). Trdim, da bi morali okrepiti pojem, da == se lahko uporabi namesto Enum.equals (Object). Razloge za to trditev podajam v nadaljevanju tega prispevka.

Obstajajo štirje razlogi, za katere verjamem, da jih uporabljam == za primerjavo Java enumov je skoraj vedno bolje za uporabo metode "enako":

  1. The == on enums zagotavlja enako pričakovano primerjavo (vsebino) kot enako
  2. The == on enums je verjetno bolj berljiv (manj podroben) kot enako
  3. The == on enums je bolj varno kot enako
  4. The == on enums namesto preverjanja med izvajanjem zagotavlja preverjanje v času prevajanja (statično)

Zgoraj našteti drugi razlog ("verjetno bolj berljiv") je očitno stvar mnenja, toda za tisti del o "manj podrobnosti" se lahko dogovorimo. Prvi razlog, ki ga imam na splošno raje == primerjava enumov je posledica tega, kako specifikacija jezika Java opisuje enume. Oddelek 8.9 ("Enum") določa:

Napaka pri času prevajanja je, če poskušamo eksplicitno izdelati primerek tipa enum. Končna metoda kloniranja v Enumu zagotavlja, da konstant enum ni mogoče nikoli klonirati, posebna obdelava z mehanizmom serializacije pa zagotavlja, da podvojeni primerki nikoli ne nastanejo kot posledica deserializacije. Odsevni primerek vrst enum je prepovedan. Te štiri stvari skupaj zagotavljajo, da ne obstajajo nobeni primeri enum tipa, razen tistih, ki jih določajo konstante enum.

Ker obstaja samo en primerek vsake enumne konstante, je dovoljeno uporabiti operator == namesto metode equals pri primerjavi dveh referenc objekta, če je znano, da se vsaj eden od njih nanaša na enum konstanto. (Metoda enakosti v Enumu je končna metoda, ki na svoj argument zgolj prikliče super.equals in vrne rezultat, s čimer izvede primerjavo identitete.)

Izvleček iz zgornje specifikacije kaže in nato izrecno navaja, da je varno uporabljati == operater za primerjavo dveh enumov, ker ni nobenega načina, da bi lahko bilo več primerov iste konstante enum.

Četrta prednost == konec .equals pri primerjavi enumov gre za varnost pri prevajanju. Uporaba == prisili strožje preverjanje časa prevajanja kot tisto za .equals ker mora Object.equals (Object) po pogodbi sprejeti poljubno Predmet. Ko uporabljam statično tipkan jezik, kot je Java, verjamem, da čim bolj izkoristim prednosti tega statičnega tipkanja. V nasprotnem primeru bi uporabil dinamično tipkan jezik. Verjamem, da je ena od ponavljajočih se tem učinkovite Jave ravno to: raje preverite statično vrsto, kadar je le mogoče.

Denimo, da sem imel poklicano nabrajanje po meri Sadje in poskusil sem ga primerjati z razredom java.awt.Color. Uporabljati == operater mi omogoča, da dobim napako v času prevajanja (vključno z vnaprejšnjim obvestilom v moji najljubši Java IDE) težave. Tu je seznam kod, ki poskuša primerjati naštevanje po meri z razredom JDK z uporabo == operater:

/ ** * Navedite, če je na voljo Barva je lubenica. * * Izvedba te metode se komentira, da bi se izognili napaki prevajalnika *, ki upravičeno ne dovoljuje == primerjati dva predmeta, ki to nista in * ne moreta biti ista stvar. * * @param kandidatColor Barva, ki nikoli ne bo lubenica. * @return Nikoli ne bi smelo biti res. * / public boolean isColorWatermelon (java.awt.Color kandidatColor) {// Ta primerjava sadja z barvo bo privedla do napake prevajalnika: // napaka: neprimerljive vrste: sadje in barva vrne Fruit.WATERMELON == kandidatColor; } 

Napaka prevajalnika je prikazana na naslednjem posnetku zaslona.

Čeprav nisem navdušenec nad napakami, imam raje, da jih lovimo statično v času prevajanja in ne glede na pokritost izvajanja. Če bi uporabil enako Za to primerjavo bi bila koda dobro sestavljena, vendar bi se metoda vedno vrnila napačno false, ker ni možnosti a prah. primeri. Sadje enum bo enak a java.awt.Color razred. Ne priporočam, ampak tukaj je primerjalna metoda, ki uporablja .equals:

/ ** * Navedite, ali je navedena barva malina. To je popolna neumnost *, ker Barva nikoli ne more biti enaka Sadju, vendar prevajalnik dovoli to * preverjanje in samo določitev časa izvajanja lahko kaže, da niso * enake, čeprav nikoli ne morejo biti enake. Tako NE delati stvari. * * @param kandidatColor Barva, ki nikoli ne bo malina. * @return {@code false}. Nenehno. * / public boolean isColorRaspberry (java.awt.Color kandidatColor) {// // NE TEGA: Zapravite trud in zavajate kodo !!!!!!!! // vrnemo Fruit.RASPBERRY.equals (kandidatColor); } 

"Lepa" stvar zgoraj je pomanjkanje napak pri prevajanju. Lepo se sestavi. Na žalost se to plača s potencialno visoko ceno.

Končna prednost uporabe, ki sem jo naštel == raje kot Enum.equals pri primerjavi enumov je izogibanje strašljivemu NullPointerExceptionu. Kot sem omenil v Effective Java NullPointerException Handling, se na splošno rad izogibam nepričakovanim NullPointerExceptions. Obstaja omejen nabor situacij, v katerih resnično želim, da se obstoj niče obravnava kot izjemen primer, vendar imam pogosto raje bolj prijazno poročanje o težavi. Prednost primerjave naštevanja z == je, da je nulo mogoče primerjati z ne-null enumom, ne da bi pri tem naleteli na NullPointerException (NPE). Rezultat te primerjave je očitno napačno.

Eden od načinov, kako se izogniti NPE pri uporabi .equals (objekt) je sklicevanje na enako metoda proti enum konstanti ali znanemu ne-null enumu in nato posredujejo potencialni enum spornega znaka (po možnosti null) kot parameter enako metoda. To se že vrsto let počne v Javi z Strings, da bi se izognili NPE. Vendar pa s == operater, vrstni red primerjave ni pomemben. To mi je všeč.

Navedel sem svoje argumente in zdaj prestopim na nekaj primerov kode. Naslednji seznam predstavlja realizacijo prej omenjenega hipotetičnega naštevanja Fruit.

Sadje.java

paket prah v.primeri; javna enum Sadje {JABOLKA, BANANA, ROVNICA, BOROVNICA, ČEŠNJA, Grozdje, KIWI, MANGO, POMORANČA, MALINA, JAGODA, PARADIŽNIK, LUBENICA} 

Naslednji seznam kod je preprost razred Java, ki ponuja metode za odkrivanje, ali je določeno naštevanje ali predmet določen plod. Običajno bi takšne čeke postavil v samo naštevanje, vendar tukaj bolje delujejo v ločenem razredu za moje ilustrativne in demonstracijske namene. Ta razred vključuje dve prej prikazani metodi za primerjavo Sadje do Barva z obema == in enako. Seveda, metoda uporablja == če želite primerjati enum z razredom, je bilo treba ta del komentirati, da je pravilno sestavljen.

EnumComparisonMain.java

paket prah v.primeri; javni razred EnumComparisonMain {/ ** * Navedite, ali je sadje lubenica ({@code true} ali ne * ({@code false}). * * @param kandidatFruit Sadje, ki je lahko lubenica ali ne; null je * popolnoma sprejemljivo (vključite ga!). * @return {@code true}, če je sadje lubenica; {@code false}, če * pod pogojem, da sadje NI lubenica. * / public boolean isFruitWatermelon (Sadje kandidatFruit) {return kandidatFruit = = Fruit.WATERMELON;} / ** * Navedite, ali je predvideni objekt Fruit.WATERMELON ({@code true}) ali * ne ({@code false}). * * @Param kandidatObject Objekt, ki je lahko ali ne lubenice in * morda niti ne bo Sadje! * @ vrni {@code true}, če je navedeni objekt Fruit.WATERMELON; * {@code false}, če navedeni objekt ni Fruit.WATERMELON. * / public boolean isObjectWatermelon (Object kandidatObject ) {return kandidatObject == Fruit.WATERMELON;} / ** * Navedite, če je navedena Barva je lubenica. * * Izvajanje te metode je komentirano izogibajte se napaki prevajalnika *, ki upravičeno onemogoča == primerjavo dveh predmetov, ki to nista in * ne moreta biti ista stvar. * * @param kandidatColor Barva, ki nikoli ne bo lubenica. * @return Nikoli ne bi smelo biti res. * / public boolean isColorWatermelon (java.awt.Color kandidatColor) {// moral sem komentirati primerjavo sadja z barvo, da bi se izognili napaki prevajalnika: // napaka: neprimerljive vrste: vrnitev sadja in barve /*Fruit.WATERMELON == kandidatColor * / napačno; } / ** * Navedite, ali je sadje jagoda ({@code true}) ali ne * ({@code false}). * * @param kandidatFruit Sadje, ki je lahko jagoda ali ne; null je * popolnoma sprejemljivo (vključite ga!). * @ vrni {@code true}, če je sadje jagoda; {@code false}, če * pod pogojem, da sadje NI jagoda. * / public boolean isFruitStrawberry (Sadje kandidatFruit) {vrni Sadje.JAGA == kandidatFruit; } / ** * Navedite, ali je sadje malina ({@code true}) ali ne * ({@code false}). * * @param kandidatFruit Sadje, ki je lahko malina ali ne; null je * popolnoma in popolnoma nesprejemljivo; prosim, ne podajte nič, prosim, * prosim, prosim. * @ vrni {@code true}, če je sadje malina; {@code false}, če * pod pogojem, da sadje NI malina. * / public boolean isFruitRaspberry (Fruit kandidatFruit) {vrnitev кандидатFruit.equals (Fruit.RASPBERRY); } / ** * Navedite, ali je predmet Predmet Sadje.MALINA ({@code true}) ali * ne ({@code false}). * * @param kandidatObject Objekt, ki je lahko malina ali pa tudi ne in je morda sadje! * @return {@code true}, če je naveden Predmet je sadje.MALINA; {@code false} *, če ni sadje ali ne malina. * / public boolean isObjectRaspberry (Object kandidatObject) {vrnitev kandidatObject.equals (Fruit.RASPBERRY); } / ** * Navedite, ali je navedena barva malina. To je popolna neumnost *, ker Barva nikoli ne more biti enaka Sadju, vendar prevajalnik dovoli to * preverjanje in samo določitev časa izvajanja lahko kaže, da niso * enake, čeprav nikoli ne morejo biti enake. Tako NE delati stvari. * * @param kandidatColor Barva, ki nikoli ne bo malina. * @return {@code false}. Nenehno. * / public boolean isColorRaspberry (java.awt.Color kandidatColor) {// // TEGA NE TREBA: Zapravljanje truda in zavajajoča koda !!!!!!!! // vrnemo Fruit.RASPBERRY.equals (kandidatColor); } / ** * Navedite, ali je sadje grozdje ({@code true}) ali ne * ({@code false}). * * @param kandidatFruit Sadje, ki je lahko grozdje ali ne; null je * popolnoma sprejemljivo (vključite ga!). * @ vrni {@code true}, če je sadje grozdje; {@code false}, če * pod pogojem, da sadje NI grozdje. * / public boolean isFruitGrape (Fruit kandidatFruit) {return Fruit.GRAPE.equals (kandidatFruit); }} 

Odločil sem se, da bom do demonstracij idej, zajetih v zgornjih metodah, pristopil z enostavnimi testi. Zlasti uporabljam Groovyjev GroovyTestCase. Ta razred za uporabo enostavnega testiranja z Groovyjem je v naslednjem seznamu kod.

EnumComparisonTest.groovy

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