Programovanie

Úvahy o Java toString ()

Dokonca aj začínajúci vývojári v odbore Java vedia o užitočnosti metódy Object.toString (), ktorá je k dispozícii pre všetky inštancie tried Java a je možné ju prepísať, aby poskytla užitočné podrobnosti týkajúce sa konkrétnej inštancie triedy Java. Bohužiaľ, ani ostrieľaní vývojári Java občas nevyužijú všetky výhody tejto výkonnej funkcie Java z rôznych dôvodov. V tomto blogovom príspevku sa pozriem na skromnú Javu natiahnuť() a opíš jednoduché kroky, ktoré je možné podniknúť na zlepšenie utilitarizmu natiahnuť().

Explicitne implementovať (prepísať) toString ()

Možno najdôležitejšia úvaha súvisiaca s dosiahnutím maximálnej hodnoty z natiahnuť() je zabezpečiť ich implementáciu. Aj keď koreň všetkých hierarchií tried Java, Object, poskytuje implementáciu toString (), ktorá je k dispozícii pre všetky triedy Java, predvolené správanie tejto metódy nie je takmer nikdy užitočné. Javadoc pre Object.toString () vysvetľuje, čo je predvolene poskytované pre toString (), keď pre triedu nie je k dispozícii vlastná verzia:

Metóda toString pre triedu Object vracia reťazec pozostávajúci z názvu triedy, ktorej je objekt inštanciou, znaku „zavináča“ a nepodpísaného hexadecimálneho vyjadrenia hash kódu objektu. Inými slovami, táto metóda vracia reťazec rovný hodnote:getClass (). getName () + '@' + Integer.toHexString (hashCode ())

Je ťažké prísť so situáciou, v ktorej je užitočný názov triedy a hexadecimálne znázornenie hash kódu objektu oddeleného znakom @. Takmer vo všetkých prípadoch je podstatne užitočnejšie uviesť vlastný, explicitný

natiahnuť()

implementácia v triede, ktorá prepíše túto predvolenú verziu.

Javadoc pre Object.toString () nám tiež hovorí, čo a natiahnuť() implementácia by vo všeobecnosti mala obsahovať a tiež predstavuje rovnaké odporúčanie, aké tu uvádzam: prepísať natiahnuť():

Všeobecne platí, ženatiahnuť metóda vráti reťazec, ktorý "textovo predstavuje" tento objekt. Výsledkom by malo byť stručné, ale informatívne znázornenie, ktoré je pre človeka ľahko čitateľné. Odporúča sa, aby túto metódu prepísali všetky podtriedy.

Kedykoľvek píšem novú triedu, existuje niekoľko metód, ktoré pridanie považujem za súčasť aktu vytvorenia novej triedy. Tie obsahujú

hashCode ()

a

rovná sa (Objekt)

Ak je to vhodné. Podľa mojich skúseností a podľa môjho názoru však implementácia výslovná

natiahnuť()

je vždy vhodné.

Ak „odporúčanie“ Javadocu, že „všetky podtriedy prepíšu túto metódu“, nestačí (potom nepredpokladám, že moje odporúčanie je také), aby vývojárovi Java zdôvodnil dôležitosť a hodnotu explicitného natiahnuť() Metóda, potom odporúčam preskúmať Efektívnu položku Java „Vždy prepísať toString“ od Josha Blocha, aby ste získali ďalšie informácie o dôležitosti implementácie natiahnuť(). Podľa môjho názoru by všetci vývojári Java mali vlastniť jeho kópiu Efektívna Java, ale našťastie kapitola s touto položkou zapnutá natiahnuť() je k dispozícii pre tých, ktorí nevlastnia kópiu: Metódy spoločné pre všetky objekty.

Údržba / aktualizácia reťazca ()

Je frustrujúce explicitne alebo implicitne volať objekt natiahnuť() v protokolovom výpise alebo inom diagnostickom nástroji a majú vrátený predvolený názov triedy a hexadecimálny hash kód objektu, a nie niečo užitočnejšie a čitateľnejšie. Je rovnako frustrujúce mať neúplné natiahnuť() implementácia, ktorá nezahŕňa významné časti súčasných charakteristík a stavu objektu. Snažím sa byť dostatočne disciplinovaný a vytvárať si a dodržiavať zvyk neustále si prezerať natiahnuť() implementácia spolu s preskúmaním rovná sa (Objekt) a hashCode () implementácie akejkoľvek triedy, na ktorej pracujem, sú pre mňa nové alebo kedykoľvek pridávam alebo mením atribúty triedy.

Iba fakty (ale všetky / väčšina z nich!)

V kapitole Efektívna Java Bloch píše: „Ak je to praktické, mala by metóda toString vrátiť všetky zaujímavé informácie obsiahnuté v objekte.“ Môže byť bolestivé a namáhavé pridávať do svojej implementácie toString všetky atribúty triedy ťažkej s atribútmi, ale hodnota pre tých, ktorí sa snažia ladiť a diagnostikovať problémy spojené s touto triedou, bude stáť za námahu. Spravidla sa usilujem mať všetky významné nenulové atribúty svojej inštancie v generovanej reťazcovej reprezentácii (a niekedy zahŕňajú skutočnosť, že niektoré atribúty sú null). Pre atribúty typicky pridávam minimálny identifikačný text. Je to v mnohých ohľadoch viac umenie ako veda, ale snažím sa zahrnúť dostatok textu na rozlíšenie atribútov bez toho, aby som budúcim vývojárom zavadzal príliš podrobne. Najdôležitejšie pre mňa je zaviesť hodnoty atribútov a nejaký typ identifikačného kľúča.

Poznajte svoje publikum

Jedna z najbežnejších chýb, ktorú som videl v súvislosti s vývojármi Java, ktorí nezačínajú natiahnuť() zabúda na čo a na koho natiahnuť() je zvyčajne určený pre. Všeobecne, natiahnuť() je diagnostický a ladiaci nástroj, ktorý uľahčuje zaznamenávanie podrobností o konkrétnej inštancii v konkrétnom čase pre neskoršie ladenie a diagnostiku. Spravidla je chybou, aby používateľské rozhrania zobrazovali reťazcové reprezentácie generované natiahnuť() alebo robiť logické rozhodnutia na základe a natiahnuť() reprezentácia (v skutočnosti je logické rozhodovanie o ľubovoľnom reťazci krehké!). Videl som dobre mienených vývojárov natiahnuť() vráti formát XML na použitie v niektorých ďalších aspektoch kódu vhodných pre XML. Ďalšou významnou chybou je prinútiť klientov analyzovať String vrátený z natiahnuť() za účelom programového prístupu k dátovým členom. Pravdepodobne je lepšie poskytnúť verejnú metódu getra / accessoru, ako sa spoliehať na natiahnuť() nikdy sa nemení. Všetko sú to chyby, pretože tieto prístupy zabúdajú na úmysel a natiahnuť() implementácia. Toto je obzvlášť zákerné, ak vývojár odstráni dôležité vlastnosti z natiahnuť() metóda (pozri poslednú položku), aby to na používateľskom rozhraní vyzeralo lepšie.

mám rád natiahnuť() implementácie, aby mali všetky príslušné podrobnosti a aby poskytovali minimálne formátovanie, aby boli tieto detaily chutnejšie. Toto formátovanie môže obsahovať uvážlivo vybrané nové riadkové znaky [System.getProperty ("line.seperator");] a tabulátory, dvojbodky, bodkočiarky atď. Neinvestujem toľko času, ako by som chcel do výsledku predloženého koncovému používateľovi softvéru, ale snažím sa, aby formátovanie bolo dosť pekné na to, aby bolo viac čitateľné. Snažím sa realizovať natiahnuť() metódy, ktoré nie sú príliš komplikované alebo nákladné na údržbu, ale ktoré poskytujú veľmi jednoduché formátovanie. Snažím sa správať k budúcim správcom môjho kódu tak, ako by som chcel, aby s nimi zaobchádzali vývojári, ktorých kód jedného dňa budem udržiavať.

Vo svojej položke dňa natiahnuť() Bloch uvádza, že vývojár by si mal zvoliť, či bude alebo nebude mať implementáciu natiahnuť() vrátiť konkrétny formát. Ak je určený konkrétny formát, mal by to byť zdokumentovaný v komentároch Javadocu a Bloch ďalej odporúča, aby bol k dispozícii statický inicializátor, ktorý dokáže vrátiť objektu jeho inštančné charakteristiky na základe reťazca vygenerovaného natiahnuť(). S týmto všetkým súhlasím, ale verím, že to je viac problémov, ako je väčšina vývojárov ochotná podstúpiť. Bloch tiež poukazuje na to, že akékoľvek zmeny tohto formátu v budúcich vydaniach spôsobia ľuďom bolesť a úzkosť v závislosti od toho (z tohto dôvodu si nemyslím, že je dobrý nápad mať logiku závislú od natiahnuť() výkon). S významnou disciplínou pri písaní a udržiavaní príslušnej dokumentácie, s preddefinovaným formátom pre a natiahnuť() môže byť pravdepodobné. Zdá sa mi však ako problém a lepšie jednoducho vytvoriť novú a samostatnú metódu pre takéto použitie a ponechať natiahnuť() nezaťažený.

Nie sú tolerované žiadne vedľajšie účinky

Rovnako dôležité ako natiahnuť() implementácia je, je všeobecne neprijateľné (a určite sa považuje za zlú formu) mať explicitné alebo implicitné volanie natiahnuť() mať dopad na logiku alebo viesť k výnimkám alebo logickým problémom. Autor a natiahnuť() metóda by mala byť opatrná, aby zabezpečila, že referencie sú skontrolované na hodnotu null pred prístupom k nim, aby sa zabránilo NullPointerException. Mnoho taktík, ktoré som popísal v príspevku Effective Java NullPointerException Handling, sa dá použiť v natiahnuť() implementácia. Napríklad String.valueOf (Object) poskytuje jednoduchý mechanizmus pre nulovú bezpečnosť atribútov pochybného pôvodu.

Je to rovnako dôležité pre natiahnuť() vývojár, aby skontroloval veľkosti polí a iné veľkosti kolekcie predtým, ako sa pokúsi získať prístup k prvkom mimo tejto kolekcie. Najmä je príliš ľahké naraziť na StringIndexOutOfBoundsException, keď sa pokúšate manipulovať s hodnotami String pomocou String.substring.

Pretože objekt natiahnuť() implementáciu je možné ľahko vyvolať bez toho, aby si to vývojár svedomite uvedomil, je obzvlášť dôležitá táto rada, aby sa vyhlo výnimkám alebo logike (najmä logike zmeny stavu). Posledná vec, ktorú chce ktokoľvek, je nechať akt logovania aktuálneho stavu inštancie viesť k výnimke alebo zmene stavu a správania. A natiahnuť() implementácia by mala byť efektívne iba na čítanie, v ktorej sa načíta stav objektu, aby sa vygeneroval reťazec pre návrat. Ak sa v procese zmenia akékoľvek atribúty, je pravdepodobné, že v nepredvídateľnom čase nastanú zlé veci.

Moja pozícia je taká, že a natiahnuť() implementácia by mala obsahovať iba stav vo vygenerovanom reťazci, ktorý je prístupný v rovnakom priestore procesu v čase jeho generovania. Pre mňa nie je obhájiteľné mať natiahnuť() implementácia prístupu k vzdialeným službám na vytvorenie reťazca inštancie. Možno o niečo menej zrejmé je, že inštancia by nemala vypĺňať atribúty údajov, pretože natiahnuť() bol volaný. The natiahnuť() implementácia by mala obsahovať iba správu o tom, ako sa veci majú v súčasnosti, a nie o tom, ako by mohli alebo budú v budúcnosti, ak nastanú určité odlišné scenáre alebo ak sa veci načítajú. Aby boli efektívne pri ladení a diagnostike, natiahnuť() musí ukázať, aké sú podmienky a nie aké by mohli byť.

Jednoduché formátovanie je úprimne ocenené

Ako je popísané vyššie, rozumné použitie oddeľovačov riadkov a tabulátorov môže byť užitočné na zvýšenie chutnosti dlhých a zložitých inštancií pri generovaní vo formáte reťazca. Existujú aj ďalšie „triky“, ktoré môžu urobiť veci krajšími. Nielenže to robí String.valueOf (objekt) poskytnúť nejaké nulový ochrana, ale aj predstavuje nulový ako reťazec "null" (ktorý je často preferovanou reprezentáciou nuly v reťazci generovanom toString (). Arrays.toString (Object) je užitočný na ľahké predstavenie polí ako reťazcov (ďalšie podrobnosti nájdete v mojom príspevku Stringifying Java Arrays).

Zahrnúť názov triedy do reprezentácie toString

Ako je opísané vyššie, predvolená implementácia natiahnuť() poskytuje názov triedy ako súčasť reprezentácie inštancie. Keď to výslovne prepíšeme, potenciálne stratíme tento názov triedy. Toto zvyčajne nie je veľká záležitosť, ak sa prihlasuje reťazec inštancie, pretože protokolovací rámec bude obsahovať názov triedy. Radšej však pracujem na bezpečnej strane a vždy mám k dispozícii názov triedy. Nezaujíma ma zachovanie reprezentácie hexadecimálneho hash kódu z predvolenej hodnoty natiahnuť(), ale názov triedy môže byť užitočný. Dobrým príkladom toho je Throwable.toString (). Dávam prednosť použitiu tejto metódy skôr ako getMessage alebo getLocalizedMessage, pretože prvá (natiahnuť()) zahŕňa Hoditeľnénázov triedy, zatiaľ čo posledné dve metódy nie.

toString () alternatívy

Momentálne to nemáme (aspoň nie štandardný prístup), ale hovorilo sa o triede objektov v Jave, ktorá by prešla bezpečnou a užitočnou prípravou reťazcových reprezentácií rôznych objektov, dátových štruktúr a zbierok. Nepočul som o žiadnom nedávnom pokroku v JDK7 v tejto triede. Štandardná trieda v JDK, ktorá poskytovala reťazcovú reprezentáciu objektov, aj keď definície triedy objektov neposkytovali explicitný údaj natiahnuť() by bolo užitočné.

Apache Commons ToStringBuilder môže byť najpopulárnejším riešením na vytváranie bezpečných implementácií toString () s niektorými základnými ovládacími prvkami formátovania. Už predtým som blogoval na ToStringBuilder a existuje veľa ďalších online zdrojov týkajúcich sa používania ToStringBuilder.

Technický tip spoločnosti Glen McCluskey na Java Technology „Writing toString Methods“ poskytuje ďalšie podrobnosti o tom, ako napísať dobrú metódu toString (). V jednom z komentárov čitateľov Giovanni Pelosi uvádza preferenciu pre delegovanie výroby reťazcového znázornenia inštancie v rámci hierarchií dedičstva z natiahnuť() do triedy delegátov postavenej na tento účel.

Záver

Myslím si, že väčšina vývojárov Java uznáva hodnotu dobra natiahnuť() implementácie. Bohužiaľ, tieto implementácie nie sú vždy také dobré alebo užitočné, ako by mohli byť. V tomto príspevku som sa pokúsil načrtnúť niektoré úvahy týkajúce sa zlepšenia natiahnuť() implementácie. Aj keď a natiahnuť() metóda nebude (alebo by aspoň nemala) mať dopad na logiku ako rovná sa (Objekt) alebo hashCode () Táto metóda môže vylepšiť ladenie a diagnostickú účinnosť. Menej času stráveného zisťovaním, aký je stav objektu, znamená viac času na riešenie problému, prechod na zaujímavejšie výzvy a uspokojenie ďalších potrieb klientov.

Tento príbeh, „Úvahy o Java toString ()“, bol pôvodne publikovaný spoločnosťou JavaWorld.

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