Programavimas

Norėdami palyginti „Java Enums“, naudokite == (arba! =)

Dauguma naujų „Java“ kūrėjų greitai sužino, kad jie paprastai turėtų palyginti „Java“ eilutes naudodami „String.equals“ („Object“), o ne naudodami ==. Tai pakartotinai pabrėžiama ir sustiprinama naujiems kūrėjams, nes jie beveik visada reiškia palyginti eilutės turinį (faktinius simbolius, sudarančius eilutę), o ne eilutės tapatybę (jos adresą atmintyje). Aš tvirtinu, kad turėtume sustiprinti mintį == gali būti naudojamas vietoj Enum.equals (Object). Šio teiginio motyvus pateikiu likusioje šio įrašo dalyje.

Manau, kad yra keturios priežastys == palyginti Java enums yra beveik visada geriau naudoti metodą „lygu“:

  1. == dėl enums pateikia tą patį numatomą palyginimą (turinį) kaip ir lygu
  2. == dėl enums yra neabejotinai lengviau skaitomas (mažiau žodinis) nei lygu
  3. == enums yra niekiniu požiūriu saugesnis nei lygu
  4. == on enums teikia kompiliavimo laiko (statinį) tikrinimą, o ne vykdymo laiką

Antroji aukščiau išvardyta priežastis („neabejotinai labiau skaitoma“) akivaizdžiai priklauso nuo nuomonės, tačiau galima sutikti dėl tos dalies, kuri pasakoja apie „mažiau daugžodžius“. Pirma priežastis, dėl kurios man labiau patinka == lyginant „enums“ yra „Java“ kalbos specifikacijos aprašymo „enums“ pasekmė. 8.9 skirsnyje („Enums“) sakoma:

Tai yra kompiliavimo laiko klaida, kai bandoma aiškiai įvesti enum tipą. Galutinis klonų metodas „Enum“ užtikrina, kad „enum“ konstantos niekada nebus klonuojamos, o specialus serializavimo mechanizmas užtikrina, kad dezerializacijos metu niekada nesikurtų pasikartojantys egzemplioriai. Reflektyvus enum tipų eksponavimas draudžiamas. Šie keturi dalykai kartu užtikrina, kad nėra jokių enum tipo egzempliorių, išskyrus tuos, kuriuos apibrėžia enum konstantos.

Kadangi kiekvienoje enumo konstantoje yra tik vienas egzempliorius, lyginant dvi objekto nuorodas, vietoj lygių metodo leidžiama naudoti == operatorių, jei yra žinoma, kad bent vienas iš jų nurodo skaičiaus konstantą. („Enum“ lygių metodas yra galutinis metodas, kurio argumente paprasčiausiai iškviečiamas super.equals ir pateikiamas rezultatas, taip atliekant tapatybės palyginimą.)

Aukščiau pateiktos specifikacijos ištrauka reiškia ir tada aiškiai nurodoma, kad saugu naudoti == operatorius, norėdamas palyginti du enumus, nes jokiu būdu negali būti daugiau nei vienas tos pačios enumo konstantos egzempliorius.

Ketvirtasis pranašumas == baigėsi .lygus kai lyginamos sąskaitos yra susijusios su kompiliavimo laiko saugumu. Panaudojimas == priverčia griežtesnį kompiliavimo laiko patikrinimą nei .lygus nes Object.equals (Object) pagal sutartį turi pasirinkti savavališką Objektas. Naudodamasis statiškai įvesta kalba, pvz., „Java“, tikiu kuo labiau pasinaudoti šio statinio spausdinimo pranašumais. Kitu atveju norėčiau naudoti dinamiškai spausdintą kalbą. Manau, kad viena iš pasikartojančių „Efektyvios„ Java “temų yra būtent tokia: pirmenybę teikite statinio tipo tikrinimui, kai tik įmanoma.

Pvz., Tarkime, kad turėjau specialųjį pavadinimą Vaisiai ir bandžiau palyginti su java.awt.Spalvos klase. Naudojant == operatorius leidžia man gauti kompiliavimo laiko klaidą (įskaitant išankstinį pranešimą mano mėgstamoje „Java IDE“) apie problemą. Čia yra kodų sąrašas, bandantis palyginti pasirinktinį sąrašą su JDK klase naudojant == operatorius:

/ ** * Nurodykite, jei pateikiama. Spalva yra arbūzas. * * Šio metodo įgyvendinimas yra komentuojamas, kad būtų išvengta kompiliatoriaus klaidos *, kuri teisėtai neleidžia == palyginti dviejų objektų, kurie nėra ir * niekada negali būti tas pats dalykas. * * @param kandidatas Spalva Spalva, kuri niekada nebus arbūzas. * @return Niekada neturėtų būti tiesa. * / public boolean isColorWatermelon (java.awt.Color kandidateColor) {// Šis vaisių ir spalvų palyginimas sukurs kompiliatoriaus klaidą: // klaida: nepalyginami tipai: vaisiai ir spalvos grąžina vaisius.ATVIRMELIS == kandidato spalva; } 

Kompiliatoriaus klaida rodoma ekrano momentinėje nuotraukoje, kuri bus pateikta vėliau.

Nors nesu klaidų mėgėjas, norėčiau, kad kompiliavimo metu jos būtų užfiksuotos statiškai, o ne priklausomai nuo vykdymo laiko aprėpties. Ar aš būčiau naudojęsis lygu metodas šiam palyginimui, kodas būtų sudarytas gerai, tačiau metodas visada grįžta melagingas klaidinga, nes niekaip nėra a dustin.pavyzdžiai.Vaisiai enumas bus lygus a java.awt.Spalva klasė. Aš nerekomenduoju, bet čia yra palyginimo metodas .lygus:

/ ** * Nurodykite, ar pateikta spalva yra avietė. Tai yra visiška nesąmonė *, nes spalva niekada negali būti lygi vaisiui, tačiau kompiliatorius leidžia šį * patikrinimą ir tik vykdymo laiko nustatymas gali parodyti, kad jie nėra * lygūs, nors niekada negali būti lygūs. Taip NEGALI daryti dalykų. * * @param kandidatas Spalva Spalva, kuri niekada nebus avietė. * @return {@code false}. Visada. * / public boolean isColorRaspberry (java.awt.Color kandidateColor) {// // NEDARYKITE šito: pastangų švaistymas ir klaidinantis kodas !!!!!!!! // grįžti Vaisiai. AVALYKOS.lygūs (kandidato spalva); } 

„Gražus“ dalykas, susijęs su aukščiau išdėstytu dalyku, yra kompiliavimo laiko klaidų trūkumas. Tai gražiai sukompiliuojama. Deja, už tai sumokama už potencialiai didelę kainą.

Galutinis pranašumas, kurį išvardijau naudodamas == geriau nei Enum. lygi lyginant „enums“ yra vengimas bijoti „NullPointerException“. Kaip jau minėjau efektyviame „Java NullPointerException Handling“, man paprastai patinka vengti netikėtų dalykų „NullPointerException“s. Yra ribotas situacijų rinkinys, kai iš tikrųjų noriu, kad nulinės vertės egzistavimas būtų traktuojamas kaip išskirtinis atvejis, tačiau dažnai man labiau patinka grakščiau pranešti apie problemą. Privalumas lyginant sąskaitas su == yra tai, kad nulį galima palyginti su nulio nuliniu skaičiumi nesusidūrus su „NullPointerException“ (NPE). Šio palyginimo rezultatas akivaizdžiai yra melagingas.

Vienas iš būdų išvengti NPE naudojant .equals (objektas) yra kreiptis į lygu metodas prieš enum konstantą arba žinomą nulio nulinę enum ir tada perduoda potencialų abejotino pobūdžio (galbūt nulinio) skaičių kaip parametrą lygu metodas. Tai dažnai buvo daroma daugelį metų „Java“ naudojant „Strings“, kad būtų išvengta NPE. Tačiau su == operatorius, palyginimo tvarka neturi reikšmės. Man tai patinka.

Aš išsakiau savo argumentus ir dabar pereinu prie kelių kodų pavyzdžių. Kitas sąrašas yra anksčiau minėto hipotetinio vaisių skaičiaus įgyvendinimas.

Vaisiai.java

pakuotė dustin.pavyzdžiai; visuomenės enum Vaisiai {obuoliai, bananai, juodmedžiai, mėlynės, vyšnios, vynuogės, kiwi, mango, apelsinai, avietės, braškės, pomidorai, arbūzai} 

Kitas kodų sąrašas yra paprasta „Java“ klasė, kurioje pateikiami metodai, kaip nustatyti, ar tam tikras enumas ar objektas yra tam tikras vaisius. Tokius čekius paprastai dėdavau į patį sąrašą, bet jie geriau veikia atskiroje klasėje mano iliustraciniais ir demonstraciniais tikslais. Ši klasė apima du anksčiau palyginimo metodus Vaisiai į Spalva su abiem == ir lygu. Žinoma, metodas naudojant == norint palyginti sąrašą su klase, reikėjo pakomentuoti tą dalį, kad būtų tinkamai sudaryta.

„EnumComparisonMain.java“

pakuotė dustin.pavyzdžiai; public class EnumComparisonMain {/ ** * Nurodykite, ar pateikiami vaisiai yra arbūzas ({@code true}, ar ne * ({@code false}). * * @param kandidatasVaisių vaisiai, kurie gali būti arbūzai, bet gali būti ne; null yra * visiškai priimtina (atsineškite!). * @return {@code true}, jei vaisiai yra arbūzai; {@code false} jei * jei vaisiai NĖRA arbūzas. * / public boolean isFruitWatermelon (Fruit pretendentFruit) {return kandidatasFruit = = Fruit.WATERMELON;} / ** * Nurodykite, ar pateiktas objektas yra Fruit.WATERMELON ({@code true}) ar * ne ({@code false}). * * @Param kandidatasObject Objektas, kuris gali būti ar ne arbūzas ir jis gali būti net ne vaisius! * @return {@code true}, jei pateiktas objektas yra vaisius. ARBŪZIS; * {@code false} jei pateiktas objektas nėra „Fruit.WATERMELON“. * / public loginis „isObjectWatermelon“ („Object kandidateObject“) ) {return kandidatasObject == Vaisiai. ARBŪZAS;} / ** * Nurodykite, jei pateikiama. Spalva yra arbūzas. * * Šio metodo įgyvendinimas pakomentuotas venkite kompiliatoriaus klaidos *, kuri teisėtai neleidžia == palyginti dviejų objektų, kurie nėra ir * niekada negali būti tas pats dalykas. * * @param kandidatas Spalva Spalva, kuri niekada nebus arbūzas. * @return Niekada neturėtų būti tiesa. * / public boolean isColorWatermelon (java.awt.Color kandidateColor) {// Turėjau pakomentuoti vaisių ir spalvų palyginimą, kad išvengtumėte kompiliatoriaus klaidos: // klaida: nepalyginami tipai: vaisių ir spalvų grąža /*Fruit.WATERMELON == kandidato spalva * / klaidinga; } / ** * Nurodykite, ar pateikti vaisiai yra braškės ({@code true}), ar ne * ({@code false}). * * @param kandidatasVaisių vaisiai, kurie gali būti braškės, bet ne; null yra * visiškai priimtinas (pareikšti!). * @return {@code true}, jei vaisiai yra braškiniai; {@code false} jei * pateikiami vaisiai NE braškės. * / public boolean isFruitStrawberry (vaisių kandidatasVaisiai) {return Fruit.STRAWBERRY == kandidatasVaisiai; } / ** * Nurodykite, ar pateikti vaisiai yra avietės ({@code true}), ar ne * ({@code false}). * * @param kandidatasVaisių vaisiai, kurie gali būti aviečių, bet ne; null yra * visiškai ir visiškai nepriimtinas; prašau nepraeiti, prašau, * prašau, prašau. * @return {@code true}, jei vaisiai yra avietiniai; {@code false} jei * pateikti vaisiai NĖRA avietiniai. * / public boolean isFruitRaspberry (vaisių kandidatasVaisiai) {return kandidatasFruit.equals (Fruit.RASPBERRY); } / ** * Nurodykite, ar pateiktas objektas yra vaisius. AVASONĖ ({@code true}) ar * ne ({@code false}). * * @param kandidatasObject Objektas, kuris gali būti avietė, gali būti ir ne, ir gali būti vaisius! * @return {@code true}, jei pateikiama, kad objektas yra vaisius. AVĖŽĖS; {@code false} *, jei tai ne vaisius ar ne avietė. * / public loginis isObjectRaspberry (Object kandidatasObject) {grąžinti kandidatąObject.equals (vaisius.RASPBERRY); } / ** * Nurodykite, ar pateikta spalva yra avietė. Tai yra visiška nesąmonė *, nes spalva niekada negali būti lygi vaisiui, tačiau kompiliatorius leidžia šį * patikrinimą ir tik vykdymo laiko nustatymas gali parodyti, kad jie nėra * lygūs, nors niekada negali būti lygūs. Taip NEGALI daryti dalykų. * * @param kandidatas Spalva Spalva, kuri niekada nebus avietė. * @return {@code false}. Visada. * / public boolean isColorRaspberry (java.awt.Color kandidateColor) {// // NEDARYKITE šito: pastangų švaistymas ir klaidinantis kodas !!!!!!!! // grįžti Vaisiai. AVALYKOS.lygūs (kandidato spalva); } / ** * Nurodykite, ar pateikti vaisiai yra vynuogės ({@code true}), ar ne * ({@code false}). * * @param kandidatasVaisių vaisiai, kurie gali būti vynuogės, bet ne; null yra * visiškai priimtinas (pareikšti!). * @return {@code true}, jei vaisiai yra vynuogės; {@code false} jei * pateikti vaisiai NĖRA vynuogės. * / public boolean isFruitGrape (vaisių kandidatasVaisiai) {return Fruit.GRAPE.equals (kandidatasVaisiai); }} 

Aš nusprendžiau kreiptis į minėtų metodų idėjų demonstravimą atlikdamas vieneto testus. Visų pirma naudojuosi „Groovy“ „GroovyTestCase“. Ta klasė, skirta naudoti „Groovy“ valdomus įrenginius, yra kitame kodų sąraše.

„EnumComparisonTest.groovy“