Razmatranja Java toString ()

Čak i Java programeri svjesni su korisnosti metode Object.toString () koja je dostupna svim instancama Java klasa i može se nadjačati kako bi se pružile korisne pojedinosti o bilo kojoj određenoj instanci Java klase. Nažalost, čak i sezonski programeri Jave povremeno ne koriste u potpunosti prednost ove moćne Java značajke iz različitih razloga. U ovom blogu gledam skromnu Javu toString()i opisujem jednostavne korake koje možemo poduzeti za poboljšanje utilitarizma toString().

Eksplicitno implementirati (nadjačati) toString ()

Možda najvažnije razmatranje vezano za postizanje maksimalne vrijednosti toString()jest pružanje njihovih implementacija. Iako korijen svih hijerarhija Java klasa, Object, pruža implementaciju toString () koja je dostupna svim Java klasama, zadano ponašanje ove metode gotovo nikad nije korisno. Javadoc za Object.toString () objašnjava što je prema zadanim postavkama predviđeno za toString () kada prilagođena verzija nije navedena za klasu:

Metoda toString za klasu Objekt vraća niz koji se sastoji od imena klase čiji je objekt instanca, znaka at-sign `@ 'i nepodpisane heksadecimalne reprezentacije hash koda objekta. Drugim riječima, ova metoda vraća niz jednak vrijednosti: getClass().getName() + '@' + Integer.toHexString(hashCode())

Teško je doći do situacije u kojoj su korisni naziv klase i heksadecimalni prikaz hash koda objekta odvojeni znakom @. U gotovo svim je slučajevima znatno korisnije pružiti prilagođeni, eksplicitni

toString()

implementacija u klasi za nadjačavanje ove zadane verzije.

Javadoc za Object.toString () također nam govori što bi toString()implementacija općenito trebala podrazumijevati, a također daje istu preporuku koju dajem ovdje: override toString():

Općenito,  toString metoda vraća niz koji "tekstualno predstavlja" ovaj objekt. Rezultat bi trebao biti sažet, ali informativan prikaz koji je čovjeku lako pročitati. Preporučuje se da sve potklase nadjačaju ovu metodu.

Kad god napišem novi razred, postoji nekoliko metoda koje smatram dodavanjem kao dijelom čina stvaranja nove klase. Tu spadaju

hashCode ()

i

jednako (objekt)

ako je prigodno. Međutim, prema mom iskustvu i po mom mišljenju, provođenje eksplicitno

toString()

je uvijek prikladno.

Ako Javadoc "preporuka" da "sve potklase nadjačavaju ovu metodu" nije dovoljna (tada ne pretpostavljam da je i moja preporuka) da Java programeru opravda važnost i vrijednost eksplicitne toString()metode, onda preporučujem pregled Josha Blochova učinkovita Java stavka "Uvijek nadjačaj toString" za dodatnu pozadinu o važnosti implementacije toString(). Moje je mišljenje da bi svi programeri Jave trebali posjedovati kopiju Učinkovite Jave , ali na sreću poglavlje s ovom stavkom toString()dostupno je onima koji ne posjeduju kopiju: Metode zajedničke svim objektima.

Održavanje / ažuriranje toString ()

Frustrirajuće je izričito ili implicitno pozivanje objekta toString()u zapisniku dnevnika ili drugom dijagnostičkom alatu i vraćanje zadanog naziva klase i heksidcimalnog hash koda objekta, a ne nešto korisnije i čitljivije. Gotovo je jednako frustrirajuće imati nepotpunu toString()provedbu koja ne uključuje značajne dijelove trenutnih karakteristika i stanja objekta. Pokušavam biti dovoljno discipliniran i stvoriti i slijediti naviku uvijek pregledavati toString()implementaciju, zajedno s pregledom equals(Object)i hashCode()implementacijama bilo koje klase na kojoj radim, a koja mi je nova ili kad dodam ili promijenim atribute klase.

Samo činjenice (ali sve / većinu njih!)

U poglavlju Učinkovita Javaprethodno spomenuto, Bloch piše: "Kad je praktično, metoda toString treba vratiti sve zanimljive informacije sadržane u objektu." Može biti bolno i zamorno dodavati sve atribute klase teške atributima u njezinu implementaciju toStringa, ali vrijednost za one koji pokušavaju otkloniti pogreške i dijagnosticirati probleme povezane s tom klasom vrijedit će truda. Tipično nastojim imati sve značajne ne-null atribute svoje instance u generiranom Stringu (a ponekad uključuju i činjenicu da su neki atributi null). Također obično dodajem minimalni identifikacijski tekst za atribute. To je u mnogočemu više umjetnost nego znanost, ali pokušavam uključiti dovoljno teksta da se atributi razlikuju, a da se budući programeri ne utapaju s previše detalja. Najvažnije mi je dobiti atribute 'vrijednosti i neku vrstu identifikacijskog ključa na mjestu.

Upoznaj svoju publiku

Jedna od najčešćih pogrešaka koju sam vidio kod programera Java koji nisu početnici toString()je zaboravljanje čemu je i kome toString()obično namijenjeno. Općenito toString()je alat za dijagnostiku i uklanjanje pogrešaka koji olakšava evidentiranje detalja na određenom primjeru u određeno vrijeme za kasnije uklanjanje pogrešaka i dijagnostiku. Tipično je pogreška ako korisničko sučelje prikazuju nizove reprezentacije koje generira toString()ili donose logičke odluke na temelju toString()prikaza (zapravo, donošenje logičkih odluka na bilo kojem nizu je krhko!). Vidio sam da dobronamjerni programeri toString()vraćaju XML format za upotrebu u nekim drugim aspektima koda prilagođenim XML-u. Druga značajna pogreška je prisiljavanje klijenata da raščlanjuju niz iz kojeg se vratiotoString()radi programskog pristupa članovima podataka. Vjerojatno je bolje pružiti metodu javnog dohvatača / pristupnika nego se oslanjati na toString()nikad promjenjive. Sve su to pogreške jer ti pristupi zaboravljaju namjeru toString()provedbe. To je posebno podmuklo ako programer ukloni važne karakteristike toString()metode (vidi zadnju stavku) kako bi izgledala bolje na korisničkom sučelju.

Volim da toString()implementacije imaju sve relevantne detalje i omoguće minimalno oblikovanje kako bi ti detalji postali ukusniji. Ovo oblikovanje može uključivati ​​razumno odabrane znakove novog retka [ System.getProperty("line.seperator");] i kartice, dvotačke, zarez i sl. Ne ulažem isto toliko vremena koliko bih ulagao u rezultat predstavljen krajnjem korisniku softvera, ali pokušavam kako bi oblikovanje bilo dovoljno lijepo da bude čitljivije. Pokušavam primijeniti toString()metode koje nisu pretjerano komplicirane ili skupe za održavanje, ali koje pružaju vrlo jednostavno formatiranje. Pokušavam se ponašati prema budućim održavačima svog koda kao što bih želio da se prema meni odnose programeri čiji ću kôd jednog dana održavati.

U svojoj stavci o toString()implementaciji, Bloch navodi da bi programer trebao odabrati hoće li toString()vratiti određeni format ili ne . Ako je namijenjen određeni format, to bi trebalo dokumentirati u komentarima Javadoca, a Bloch dalje preporučuje da se osigura statički inicijalizator koji može vratiti objekt karakteristikama instance na temelju niza koji generira toString(). Slažem se sa svime ovim, ali vjerujem da je ovo veći problem nego što je većina programera spremna ići. Bloch također ističe da će bilo kakve promjene ovog formata u budućim izdanjima uzrokovati bol i bijes kod ljudi koji ovise o njemu (zbog čega mislim da nije dobra ideja da logika ovisi o toString()izlazu). Sa značajnom disciplinom pisanja i održavanja odgovarajuće dokumentacije, imajući unaprijed definirani format za atoString()mogao biti vjerojatan. Međutim, čini mi se nevolja i bolje je jednostavno stvoriti novu i zasebnu metodu za takvu upotrebu i ostaviti toString()neopterećeno.

Nema dopuštenih nuspojava

Koliko god je toString()provedba važna , općenito je neprihvatljivo (i zasigurno se smatra lošim oblikom) eksplicitni ili implicitni poziv toString()logike utjecaja ili dovesti do izuzetaka ili logičkih problema. Autor toString()metode trebao bi biti oprezan kako bi osigurao provjeru referenci za nulu prije pristupanja njima kako bi izbjegao NullPointerException. Mnoge taktike koje sam opisao u postu Effective Java NullPointerException Handling mogu se koristiti u toString()provedbi. Na primjer, String.valueOf (Object) pruža jednostavan mehanizam za nulu sigurnost atributa sumnjivog podrijetla.

Slično je i za toString()programera da provjeri veličine polja i druge veličine zbirke prije nego što pokuša pristupiti elementima izvan te zbirke. Konkretno, previše je lako naletjeti na StringIndexOutOfBoundsException kada pokušavate manipulirati vrijednostima Stringa pomoću String.substring.

Budući da se toString()implementacija objekta može lako pozvati bez da ga programer savjesno shvati, ovaj je savjet da se osigura da ne izuzima iznimke i ne izvodi logiku (posebno logiku koja mijenja stanje) posebno važan. Posljednje što bilo tko želi jest da postupak evidentiranja trenutnog stanja instance vodi ka iznimci ili promjeni stanja i ponašanja. toString()Provedba trebala učinkovito biti samo za čitanje operacije u kojoj je objektivno stanje pročitati generirati niz za povratak. Ako se u procesu promijene bilo koji atributi, vjerojatno će se dogoditi loše stvari u nepredvidivo vrijeme.

Moj je stav da bi toString()implementacija u generirani niz trebala sadržavati samo stanje koje je dostupno u istom procesnom prostoru u vrijeme njegovog generiranja. Za mene nije branjivo imati toString()implementaciju koja pristupa udaljenim uslugama za izgradnju niza instance. Možda je malo manje očito da instanca ne bi trebala popunjavati atribute podataka jer toString()je pozvana. toString()Provedbe treba izvijestiti samo o tome kako su stvari u trenutnom slučaju, a ne o tome kako oni mogu biti ili će biti u budućnosti, ako neke različite scenarije pojaviti ili ako se stvari učitava. Da bi bili učinkoviti u otklanjanju pogrešaka i dijagnostici, toString()potrebno je pokazati kakvi su uvjeti, a ne kakvi bi mogli biti.

Jednostavno oblikovanje se iskreno cijeni

Kao što je gore opisano, razborita upotreba separatora linija i kartica može biti korisna za stvaranje dugotrajnijih i složenijih primjeraka ugodnijim kada se generiraju u String formatu. Postoje i drugi "trikovi" koji mogu stvari učiniti ljepšima. Ne samo da String.valueOf(Object)pruža određenu nullzaštitu, već se predstavlja i nullkao "null" niza (što je često preferirani prikaz nule u nizu generiranom toString (). Arrays.toString (Object) koristan je za jednostavno predstavljanje nizova kao nizovi ( za dodatne pojedinosti pogledajte moj post Stringificiranje Java nizova).

Uključite naziv klase u predstavljanje toStringa

Kao što je gore opisano, zadana implementacija toString()pruža naziv klase kao dio predstavljanja instance. Kada to izričito poništimo, potencijalno ćemo izgubiti naziv klase. To obično nije velika stvar ako bilježenje niza instance, jer će okvir za evidentiranje sadržavati ime klase. Međutim, više volim biti na sigurnoj strani i uvijek imati na raspolaganju ime klase. Ne zanima me zadržavanje heksadecimalnog prikaza hash koda iz zadanog toString(), ali naziv klase može biti koristan. Dobar primjer za to je Throwable.toString (). Radije koristim tu metodu nego getMessage ili getLocalizedMessage jer prva ( toString()) sadrži Throwablenaziv klase ', dok posljednje dvije metode ne.

toString () Alternative

Trenutno to nemamo (barem ne o standardnom pristupu), ali bilo je govora o klasi Objects na Javi koja bi dugo išla prema sigurnoj i korisnoj pripremi String reprezentacija različitih objekata, struktura podataka i kolekcija. Nisam čuo za nedavni napredak u JDK7 u ovoj klasi. Korisna bi bila standardna klasa u JDK koja je pružala String prikaz objekata čak i kada definicije klasa objekata nisu eksplicitno dale toString().

Apache Commons ToStringBuilder može biti najpopularnije rješenje za izgradnju sigurnih implementacija toString () s nekim osnovnim kontrolama oblikovanja. Već sam blogovao na ToStringBuilder i postoje brojni drugi mrežni izvori u vezi s korištenjem ToStringBuilder.

Tehnički savjet Java Technology Glena McCluskeyja "Pisanje metoda toString" pruža dodatne detalje o tome kako napisati dobru metodu toString (). U jednom od komentara čitatelja, Giovanni Pelosi navodi preferenciju za delegiranje izrade nizovog predstavljanja instance unutar hijerarhija nasljeđivanja iz toString()klase delegata izgrađene u tu svrhu.

Zaključak

Mislim da većina programera Java prepoznaje vrijednost dobrih toString()implementacija. Nažalost, ove implementacije nisu uvijek toliko dobre ili korisne koliko bi mogle biti. U ovom postu pokušao sam iznijeti neka razmatranja za poboljšanje toString()implementacija. Iako toString()metoda neće (ili barem ne bi trebala) utjecati na logiku kao što to može equals(Object)ili hashCode()metoda, može poboljšati ispravljanje pogrešaka i dijagnostičku učinkovitost. Manje vremena provedenog u otkrivanju stanja objekta znači više vremena za rješavanje problema, prelazak na zanimljivije izazove i zadovoljavanje više potreba klijenta.

Ovu je priču "Razmatranja Java toString ()" izvorno objavio JavaWorld.