Programovanie

Pomocou == (alebo! =) Porovnajte výpisy Java

Väčšina nových vývojárov Java sa rýchlo dozvie, že by mali všeobecne porovnávať Java Strings pomocou String.equals (Object) a nie pomocou ==. Toto sa novým vývojárom zdôrazňuje a posilňuje opakovane, pretože takmer vždy Znamená to skôr porovnať obsah reťazca (skutočné znaky tvoriace reťazec) ako identitu reťazca (jeho adresa v pamäti). Tvrdím, že by sme mali posilniť predstavu, že == možno použiť namiesto Enum.equals (Object). Svoje odôvodnenie tohto tvrdenia uvádzam v zostávajúcej časti tohto príspevku.

Verím, že existujú štyri dôvody, ktoré používam == na porovnanie enumov Java je takmer vždy vhodnejšie ako použiť metódu „rovná sa“:

  1. The == na enums poskytuje rovnaké očakávané porovnanie (obsah) ako rovná sa
  2. The == na enums je dokázateľne čitateľnejší (menej podrobný) ako rovná sa
  3. The == na enums je bezpečnejší z hľadiska nuly ako rovná sa
  4. The == v enums poskytuje kontrolu skôr za kompilácie (statiku) ako za behu

Druhý dôvod uvedený vyššie („pravdepodobne čitateľnejší“) je zjavne vecou názoru, ale s časťou o „menej podrobnom“ je možné súhlasiť. Prvý dôvod, ktorý všeobecne uprednostňujem == pri porovnávaní enumov je dôsledok toho, ako špecifikácie jazyka Java popisuje enumy. V časti 8.9 („Enums“) sa uvádza:

Pokus o výslovné vytvorenie inštancie typu enum je chybou kompilácie. Konečná metóda klonovania v Enume zaisťuje, že konštanty výčtu nikdy nemožno klonovať, a špeciálne ošetrenie mechanizmom serializácie zaisťuje, že sa v dôsledku deserializácie nikdy nevytvoria duplicitné inštancie. Reflexné vytváranie inštancií typov enum je zakázané. Spoločne tieto štyri veci zabezpečujú, že neexistujú žiadne inštancie typu enum okrem tých, ktoré sú definované konštantami enum.

Pretože existuje iba jedna inštancia každej konštanty výčtu, je dovolené použiť operátor == namiesto metódy equals pri porovnávaní dvoch odkazov na objekty, ak je známe, že aspoň jeden z nich odkazuje na konštantu enum. (Metóda equals v Enum je konečná metóda, ktorá iba na svojom argumente vyvolá super.equals a vráti výsledok, čím sa vykoná porovnanie identity.)

Výňatok zo špecifikácie uvedenej vyššie naznačuje a potom výslovne uvádza, že je bezpečné používať == operátor na porovnanie dvoch enumov, pretože neexistuje spôsob, že môže existovať viac ako jedna inštancia rovnakej konštanty enum.

Štvrtá výhoda pre == cez .rovnaké pri porovnaní enums má čo do činenia s bezpečnosťou pri kompilácii. Použitie == vynúti prísnejšiu časovú kontrolu kompilácie ako pre .rovnaké pretože Object.equals (Object) musí byť na základe zmluvy ľubovoľný Objekt. Keď používam jazyk so statickým typom, ako je Java, verím, že čo najviac využijem výhody tohto statického písania. Inak by som použil dynamicky písaný jazyk. Verím, že jednou z opakujúcich sa tém Effective Java je práve táto: uprednostňujte kontrolu statického typu, kedykoľvek je to možné.

Predpokladajme napríklad, že som zavolal vlastný enum Ovocie a skúsil som to porovnať s triedou java.awt.Color. Pomocou == operátor mi umožňuje získať chybu kompilácie (vrátane predbežného oznámenia v mojom obľúbenom Java IDE) problému. Tu je zoznam kódov, ktorý sa pokúša porovnať vlastný enum s triedou JDK pomocou == operátor:

/ ** * Uveďte, ak je k dispozícii. Farba je melón. * * Implementácia tejto metódy je komentovaná, aby sa zabránilo chybe kompilátora *, ktorá oprávnene zakazuje == porovnávať dva objekty, ktoré nie sú a * nemôžu byť nikdy to isté. * * @param candidateColor Color, ktorá nikdy nebude melónom. * @return by nikdy nemala byť pravda. * / public boolean isColorWatermelon (java.awt.Color candidateColor) {// Toto porovnanie farby Fruit to Color povedie k chybe kompilátora: // error: neporovnateľné typy: Fruit a Color return Fruit.WATERMELON == candidateColor; } 

Chyba kompilátora sa zobrazuje na nasledujúcom snímku obrazovky.

Aj keď nie som fanúšikom chýb, dávam prednosť tomu, aby boli zachytené staticky v čase kompilácie, a nie v závislosti na časovom pokrytí. Keby som použil rovná sa metóda pre toto porovnanie, kód by bol zostavený v poriadku, ale metóda by sa vždy vrátila nepravdivé nepravda, pretože neexistuje spôsob a poprášenie.príklady.Ovocie enum sa bude rovnať a java.awt.Color trieda. Neodporúčam to, ale tu je použitá metóda porovnávania .rovnaké:

/ ** * Uveďte, či je poskytnutá farba malina. Toto je úplný nezmysel *, pretože Color sa nikdy nemôže rovnať Fruit, ale kompilátor umožňuje túto * kontrolu a iba určenie za behu môže naznačiť, že nie sú * rovnaké, aj keď nikdy nemôžu byť rovnaké. Takto NEROBTE veci. * * @param candidateColor Color, ktorá nikdy nebude malinou. * @return {@code false}. Vždy. * / public boolean isColorRaspberry (java.awt.Color candidateColor) {// // TOTO NEROBTE: Strata úsilia a zavádzajúci kód !!!!!!!! // vrátiť Fruit.RASPBERRY.equals (candidateColor); } 

„Peknou“ vecou vyššie uvedeného je nedostatok chýb v čase kompilácie. Krásne sa zostavuje. Bohužiaľ sa za to platí potenciálne vysokou cenou.

Poslednou výhodou je použitie == radšej než Enum.equals pri porovnaní enums je vyhýbanie sa obávanej NullPointerException. Ako som uviedol v Effective Java NullPointerException Handling, všeobecne sa rád vyhýbam neočakávaným NullPointerExceptions. Existuje obmedzený počet situácií, v ktorých si skutočne želám, aby sa existencia nuly považovala za výnimočný prípad, ale často uprednostňujem elegantnejšie hlásenie problému. Výhodou porovnania enumov s == je, že null sa dá porovnať s nenulovým enumom bez toho, aby sme narazili na a NullPointerException (NPE). Výsledok tohto porovnania samozrejme je nepravdivé.

Jedným zo spôsobov, ako sa vyhnúť NPE pri používaní .equals (Object) je vyvolať rovná sa metódu proti konštante enum alebo známemu nenulovému enumu a potom odovzdajte potenciálny enum sporného znaku (pravdepodobne null) ako parameter do rovná sa metóda. Toto sa často robí roky v Jave s reťazcami, aby sa zabránilo NPE. Avšak s == operátor, na poradí porovnania nezáleží. Páči sa mi to.

Urobil som svoje argumenty a teraz prejdem k niektorým príkladom kódu. Ďalším zoznamom je realizácia hypotetického ovocného výčtu uvedeného vyššie.

Ovocie.java

balenie zásypu.príklady; verejné enum Ovocie {JABLKO, BANÁNY, ČEREŠNICE, ČEREŠNICE, ČEREŠNE, HROZNU, KIWI, MANGO, ORANŽE, MALINY, JAHODY, RAJČATY, VODNÝ MELÓN} 

Ďalším výpisom kódu je jednoduchá trieda Java, ktorá poskytuje metódy na zisťovanie, či je konkrétny enum alebo objekt určitým ovocím. Bežne by som dal také kontroly do enumu, ale pre moje ilustračné a demonštračné účely tu fungujú lepšie v samostatnej triede. Táto trieda obsahuje dve metódy porovnávania uvedené vyššie Ovocie do Farba s oboma == a rovná sa. Samozrejme, že metóda pomocou == na porovnanie enumu s triedou bolo potrebné túto časť komentovať, aby sa dala správne zostaviť.

EnumComparisonMain.java

balenie zásypu.príklady; verejná trieda EnumComparisonMain {/ ** * Uveďte, či je poskytované ovocie melón ({@code true} alebo nie * ({@code false}). * * @param candidateFruit Ovocie, ktoré môže alebo nemusí byť melón; null je * úplne prijateľné (prineste to!). * @return {@code true} ak je poskytnuté ovocie je melón; {@code false} ak * ak ovocie NIE JE melón. * / public boolean isFruitWatermelon (Fruit candidateFruit) {return candidateFruit = = Fruit.WATERMELON;} / ** * Uveďte, či je poskytnutý objekt Fruit.WATERMELON ({@code true}) alebo * nie ({@code false}). * * @Param candidateObject Objekt, ktorý môže, ale nemusí byť melón a nemusí byť * ani ovocie! * @return {@code true}, ak je poskytnutý objekt Fruit.WATERMELON; * {@code false}, ak poskytnutý objekt nie je Fruit.WATERMELON. * / public boolean isObjectWatermelon (Object candidateObject ) {return candidateObject == Fruit.WATERMELON;} / ** * Uveďte, či je poskytnutá Farba je melón. * * Implementácia tejto metódy je komentovaná vyhnúť sa chybe kompilátora *, ktorá oprávnene zakazuje == porovnávať dva objekty, ktoré nie sú a * nemôžu byť nikdy to isté. * * @param candidateColor Color, ktorá nikdy nebude melónom. * @return by nikdy nemala byť pravda. * / public boolean isColorWatermelon (java.awt.Color candidateColor) {// Musel som komentovať porovnanie Fruit to Color, aby sa zabránilo chybe kompilátora: // error: neporovnateľné typy: Fruit and Color return /*Fruit.WATERMELON == candidateColor * / false; } / ** * Uveďte, či je poskytované ovocie jahoda ({@code true}) alebo nie * ({@code false}). * * @param candidateOvocie Ovocie, ktoré môže alebo nemusí byť jahoda; null je * úplne prijateľné (prineste to!). * @return {@code true}, ak je ovocie jahoda; {@code false} ak * za predpokladu, že ovocie NIE JE jahodové. * / public boolean isFruitStrawberry (Fruit candidateFruit) {návrat Fruit.STRAWBERRY == candidateFruit; } / ** * Uveďte, či je poskytované ovocie malinou ({@code true}) alebo nie * ({@code false}). * * @param candidateOvocie Ovocie, ktoré môže, ale nemusí byť malina; null je * úplne a úplne neprijateľné; prosím neprešlite null, prosím, * prosím, prosím. * @return {@code true}, ak je ovocie malina; {@code false} ak * za predpokladu, že ovocie NIE JE malina. * / public boolean isFruitRaspberry (Fruit candidateFruit) {return candidateFruit.equals (Fruit.RASPBERRY); } / ** * Uveďte, či je poskytnutý objekt Fruit.RASPBERRY ({@code true}) alebo * nie ({@code false}). * * @param candidateObject Objekt, ktorý môže alebo nemusí byť malina a môže * alebo nemusí byť ani ovocie! * @return {@code true}, ak je poskytnutý Objekt je ovocie.RASPBERRY; {@code false} * ak to nie je ovocie alebo malina. * / public boolean isObjectRaspberry (Object candidateObject) {return candidateObject.equals (Fruit.RASPBERRY); } / ** * Uveďte, či je poskytnutá farba malina. Toto je úplný nezmysel *, pretože Color sa nikdy nemôže rovnať Fruit, ale kompilátor umožňuje túto * kontrolu a iba určenie za behu môže naznačovať, že nie sú * rovnaké, aj keď nikdy nemôžu byť rovnaké. Takto NEROBTE veci. * * @param candidateColor Color, ktorá nikdy nebude malinou. * @return {@code false}. Vždy. * / public boolean isColorRaspberry (java.awt.Color candidateColor) {// // TOTO NEROBTE: Strata námahy a zavádzajúci kód !!!!!!!! // vrátiť Fruit.RASPBERRY.equals (candidateColor); } / ** * Uveďte, či je poskytované ovocie hrozno ({@code true}) alebo nie * ({@code false}). * * @param candidateOvocie Ovocie, ktoré môže, ale nemusí byť hroznom; null je * úplne prijateľné (prineste to!). * @return {@code true}, ak je ovocím hrozno; {@code false} ak * za predpokladu, že ovocie NIE JE hrozno. * / public boolean isFruitGrape (Fruit candidateFruit) {návrat Fruit.GRAPE.equals (candidateFruit); }} 

Rozhodol som sa pristúpiť k demonštrácii myšlienok zachytených vyššie uvedenými metódami prostredníctvom jednotkových testov. Využívam najmä GroovyTestCase. Táto trieda pre použitie testovania jednotiek poháňaných Groovy je v nasledujúcom zozname kódov.

EnumComparisonTest.groovy

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