Ohjelmointi

Java toString () -näkökohdat

Jopa aloittelevat Java-kehittäjät ovat tietoisia Object.toString () -menetelmän hyödyllisyydestä, joka on kaikkien Java-luokkien esiintymien käytettävissä ja joka voidaan ohittaa, jotta saadaan hyödyllisiä tietoja mistä tahansa Java-luokan esiintymästä. Valitettavasti edes kokeneet Java-kehittäjät eivät toisinaan hyödynnä tätä tehokasta Java-ominaisuutta täysimääräisesti useista syistä. Tässä blogiviestissä katson nöyrää Javaa toString () ja kuvaile helppoja vaiheita, joita voidaan tehdä parantamaan utilitarismia toString ().

Toteuta (ohitus) toString () -sovellukseen

Ehkä tärkein näkökohta, joka liittyy enimmäisarvon saavuttamiseen toString () on tarjota niiden toteutuksia. Vaikka kaikkien Java-luokkahierarkioiden pääobjekti, Object, tarjoaa toString () -toteutuksen, joka on kaikkien Java-luokkien käytettävissä, tämän menetelmän oletuskäyttäytyminen ei ole melkein koskaan hyödyllistä. Javadoc for Object.toString () selittää, mitä oletusarvoisesti tarjotaan toString (): lle, kun luokalle ei ole annettu mukautettua versiota:

Luokan Object toString-menetelmä palauttaa merkkijonon, joka koostuu sen luokan nimestä, jonka esine esiintyy, at-sign-merkistä @ ja objektin hash-koodin allekirjoittamattomasta heksadesimaalisesta esityksestä. Toisin sanoen tämä menetelmä palauttaa merkkijonon, joka on yhtä suuri kuin:getClass (). getName () + '@' + Kokonaisluku.toHexString (hashCode ())

On vaikea saada aikaan tilanne, jossa luokan nimi ja objektin hash-koodin heksadesimaalinen esitys @ -merkillä erotettuna ovat hyödyllisiä. Lähes kaikissa tapauksissa on huomattavasti hyödyllisempää tarjota mukautettu, eksplisiittinen

toString ()

luokan toteutus tämän oletusversion ohittamiseksi.

Javadoc for Object.toString () kertoo myös, mitä a toString () toteutuksen pitäisi yleensä merkitä ja antaa myös saman suosituksen, jonka annan täällä: ohittaa toString ():

Yleensämerkkijono method palauttaa merkkijonon, joka "edustaa" tekstisesti tätä objektia. Tuloksen tulisi olla ytimekäs, mutta informatiivinen esitys, jonka henkilö voi helposti lukea. On suositeltavaa, että kaikki alaluokat ohittavat tämän menetelmän.

Aina kun kirjoitan uuden luokan, on useita menetelmiä, jotka harkitsen lisäämisen osana uuden luokan luomista. Nämä sisältävät

hash koodin()

ja

on yhtä suuri (objekti)

jos soveliasta. Kokemukseni ja mielestäni kuitenkin nimenomaisen toteuttamisen

toString ()

on aina sopiva.

Jos Javadocin "suositus", jonka mukaan "kaikki alaluokat ohittavat tämän menetelmän", ei riitä (en usko, että suosittelen myöskään) perustellakseen Java-kehittäjälle nimenomaisen merkityksen ja arvon toString () -menetelmää, suosittelen tarkistamaan Josh Blochin tehokas Java-kohde "Ohita ainaString" saadaksesi lisätietoa toteutuksen tärkeydestä toString (). Olen sitä mieltä, että kaikkien Java-kehittäjien tulisi omistaa kopio Tehokas Java, mutta onneksi kappale, jossa tämä kohta on toString () on käytettävissä niille, joilla ei ole kopiota: Kaikille objekteille yhteiset menetelmät.

Ylläpitää / päivitä String ()

On turhauttavaa kutsua esineitä nimenomaisesti tai epäsuorasti toString () lokilausekkeessa tai muussa diagnostiikkatyökalussa ja palauta luokan oletusnimi ja objektin heksadesimaalinen hajautuskoodi pikemminkin kuin jotain hyödyllistä ja luettavaa. On melkein yhtä turhauttavaa olla epätäydellinen toString () toteutus, joka ei sisällä merkittäviä osia kohteen nykyisistä ominaisuuksista ja tilasta. Yritän olla riittävän kurinalainen ja luoda ja seurata tapaa tarkistaa aina toString () täytäntöönpanon lisäksi tarkistetaan on yhtä suuri (objekti) ja hash koodin() minkä tahansa työskentelemäni luokan toteutukset ovat minulle uusia tai aina kun lisäämään tai muutan luokan määritteitä.

Vain tosiasiat (mutta kaikki / suurin osa niistä!)

Luvussa Tehokas Java aiemmin mainittu, Bloch kirjoittaa: "Käytännössä toString-menetelmän tulisi palauttaa kaikki objektin sisältämät mielenkiintoiset tiedot." Voi olla tuskallista ja tylsiä lisätä kaikki määritteitä raskaan luokan määritteet sen toString-toteutukseen, mutta arvo niille, jotka yrittävät selvittää ja diagnosoida kyseiseen luokkaan liittyviä asioita, ovat vaivan arvoisia. Pyrin tyypillisesti siihen, että kaikki ilmentymän merkittävät ei-nolla-attribuutit luodaan String-esityksessä (ja joskus myös siihen, että jotkin määritteet ovat nollia). Lisään tyypillisesti myös minimaalista tunnistetekstiä määritteille. Se on monin tavoin enemmän taidetta kuin tiedettä, mutta yritän sisällyttää tarpeeksi tekstiä erottamaan määritteet ilman, että tulevia kehittäjiä sekoitetaan liian yksityiskohtaisesti. Tärkeintä on saada attribuuttien arvot ja jonkinlainen tunnistusavain paikoilleen.

Tunne yleisösi

Yksi yleisimmistä virheistä, joita olen nähnyt aloittelijoiden Java-kehittäjien tekevän toString () unohtaa mitä ja kuka toString () on tyypillisesti tarkoitettu. Yleisesti, toString () on diagnostiikka- ja virheenkorjaustyökalu, jonka avulla on helppo kirjata tietyn esiintymän yksityiskohdat tiettynä ajankohtana myöhempää virheenkorjausta ja diagnostiikkaa varten. On tyypillisesti virhe saada käyttöliittymät näyttämään merkkijonojen esitykset toString () tai tehdä loogisia päätöksiä a toString () edustus (itse asiassa loogisten päätösten tekeminen mistä tahansa merkkijonosta on hauras!). Olen nähnyt hyvää tarkoittavia kehittäjiä toString () palauta XML-muoto käytettäväksi jossakin muussa XML-ystävällisessä koodin osassa. Toinen merkittävä virhe on pakottaa asiakkaat jäsentämään palautettu merkkijono toString () tietojen jäsenten käyttämiseksi järjestelmällisesti. On luultavasti parempi tarjota julkinen getter / accessor-menetelmä kuin luottaa toString () ei koskaan muutu. Kaikki nämä ovat virheitä, koska nämä lähestymistavat unohtavat a toString () toteutus. Tämä on erityisen salakavalaa, jos kehittäjä poistaa tärkeät ominaisuudet toString () menetelmä (katso viimeinen kohta), jotta se näyttää paremmalta käyttöliittymässä.

minä pidän toString () toteutuksissa on kaikki asiaankuuluvat yksityiskohdat ja mahdollisimman pieni muotoilu näiden yksityiskohtien maittamiseksi. Tämä muotoilu voi sisältää järkevästi valittuja uusia rivimerkkejä [System.getProperty ("line.seperator");] ja välilehdet, kaksoispisteet, puolipisteet jne. En sijoita yhtä paljon aikaa kuin ohjelmiston loppukäyttäjälle esitettyyn tulokseen, mutta yritän tehdä muotoilusta riittävän mukavan, jotta se olisi enemmän luettavissa. Yritän toteuttaa toString () menetelmiä, jotka eivät ole liian monimutkaisia ​​tai kalliita ylläpitää, mutta jotka tarjoavat hyvin yksinkertaisen muotoilun. Yritän kohdella koodin tulevia ylläpitäjiä, kuten haluaisin, että kehittäjät, joiden koodin aion ylläpitää, kohdeltavat minua.

Kohteessaan toString () toteutus, Bloch toteaa, että kehittäjän tulisi valita, onko hänellä toString () palauttaa tietyn muodon. Jos tietty muoto on tarkoitettu, se on dokumentoitava Javadoc-kommenteissa, ja Bloch suosittelee lisäksi, että toimitetaan staattinen alustusohjelma, joka voi palauttaa objektin ilmentymän ominaisuuksiinsa merkkijonon perusteella, jonka toString (). Olen samaa mieltä kaikesta tästä, mutta uskon, että tämä on enemmän ongelmia kuin useimmat kehittäjät ovat halukkaita menemään. Bloch huomauttaa myös, että kaikki muutokset tähän muotoon tulevissa julkaisuissa aiheuttavat kipua ja ahdistusta ihmisille siitä riippuen (minkä vuoksi en usko, että logiikka on hyvä riippua toString () lähtö). Merkittävällä kurinalaisuudella kirjoittaa ja ylläpitää asianmukaisia ​​asiakirjoja, joilla on ennalta määritelty muoto toString () saattaa olla uskottavaa. Minusta tuntuu kuitenkin vaivalta ja parempi yksinkertaisesti luoda uusi ja erillinen menetelmä tällaisiin käyttötarkoituksiin ja jättää toString () rasittamaton.

Ei siedettyjä sivuvaikutuksia

Yhtä tärkeää kuin toString () Toteuttaminen on sitä, että ei voida hyväksyä (ja sitä pidetään varmasti huonona muotona) nimenomainen tai epäsuora kutsuminen toString () vaikuttaa logiikkaan tai johtaa poikkeuksiin tai logiikkaongelmiin. Kirjoittaja a toString () -menetelmän tulisi olla varovainen varmistaakseen, että viittaukset tarkistetaan tyhjiksi ennen niiden käyttämistä, jotta vältetään NullPointerException. Monia taktiikoita, joita kuvasin tehokkaassa Java NullPointerException -käsittelyssä, voidaan käyttää toString () toteutus. Esimerkiksi String.valueOf (Object) tarjoaa helpon mekanismin null-turvallisuuden suhteen kyseenalaisen alkuperän attribuuteissa.

Se on yhtä tärkeä toString () kehittäjä tarkistaa taulukon koot ja muut kokoelmat ennen kuin yrität käyttää elementtejä kyseisen kokoelman ulkopuolella. Erityisesti on aivan liian helppoa törmätä StringIndexOutOfBoundsExceptioniin, kun yrität manipuloida String-arvoja String.substringilla.

Koska esine on toString () toteutukseen voidaan helposti vedota ilman kehittäjän tunnollista ymmärtämistä, tämä on erityisen tärkeää, jotta varmistetaan, ettei se aiheuta poikkeuksia tai tee logiikkaa (erityisesti tilamuutoslogiikkaa). Viimeinen asia, jonka kukaan haluaa, on saada ilmentymän nykyisen tilan kirjaaminen johtamaan poikkeukseen tai tilan ja käyttäytymisen muutokseen. A toString () toteutuksen pitäisi olla tosiasiassa vain luku -operaatio, jossa objektitila luetaan tuottamaan merkkijono palautusta varten. Jos joitain ominaisuuksia muutetaan prosessin aikana, pahoja asioita tapahtuu todennäköisesti arvaamattomina aikoina.

Olen kantani, että a toString () toteutuksen tulisi sisältää vain tila generoidussa merkkijonossa, joka on käytettävissä samalla prosessitilassa sen luomisen aikana. Minulle ei ole puolustettavissa, että sinulla on toString () toteutus käyttää etäpalveluja rakentamaan ilmentymän merkkijono. Ehkä hieman vähemmän ilmeinen on, että esiintymä ei saisi täyttää tietomääritteitä, koska toString () Kutsuttiin. toString () Toteutuksen tulisi raportoida vain siitä, miten asiat ovat nykyisessä tilanteessa, eikä siitä, miten ne voivat olla tai tulevat olemaan tulevaisuudessa, jos esiintyy tiettyjä erilaisia ​​skenaarioita tai jos asiat ladataan. Ollakseen tehokas virheenkorjauksessa ja diagnostiikassa, toString () on osoitettava, kuinka olosuhteet ovat, eikä miten ne voisivat olla.

Yksinkertaista muotoilua arvostetaan vilpittömästi

Kuten edellä on kuvattu, rivien erottimien ja välilehtien järkevä käyttö voi olla hyödyllistä, kun pidennetyt ja monimutkaiset esiintymät ovat miellyttävämpiä, kun ne luodaan merkkijono-muodossa. On muitakin "temppuja", jotka voivat tehdä asioista mukavampia. Ei vain String.valueOf (objekti) tarjota joitakin tyhjä suojaa, mutta se myös tuo esiin tyhjä merkkijonona "null" (mikä on usein ensisijainen nollan esitys toString () - luomassa merkkijonossa. Arrays.toString (Object) on hyödyllinen matriisien helposti esittämiseksi merkkijonoina (lisätietoja on artikkelissani Java-matriisien merkitseminen).

Sisällytä luokan nimi toString-esitykseen

Kuten yllä on kuvattu, toString () tarjoaa luokan nimen osana instanssin esitystä. Kun ohitamme tämän nimenomaisesti, menetämme mahdollisesti tämän luokan nimen. Tämä ei ole iso juttu, jos kirjaat ilmentymän merkkijonon, koska kirjauskehys sisältää luokan nimen. Pidän kuitenkin mieluummin turvallisella puolella ja luokan nimi on aina käytettävissä. En välitä heksadesimaalisen hajakoodikoodin säilyttämisestä oletusarvosta toString (), mutta luokan nimi voi olla hyödyllinen. Hyvä esimerkki tästä on Throwable.toString (). Haluan käyttää mieluummin tätä menetelmää kuin getMessage tai getLocalizedMessage, koska edellinen (toString ()) sisältää Heitettäväluokan nimi, kun taas kaksi viimeksi mainittua menetelmää eivät.

toString () -vaihtoehdot

Meillä ei tällä hetkellä ole tätä (ainakaan tavanomaista lähestymistapaa), mutta Javassa on puhuttu Objects-luokasta, joka edistäisi pitkään turvallisten ja hyödyllisten merkkijonojen esitysten valmistelua eri objekteista, tietorakenteista ja kokoelmista. En ole kuullut JDK7: n viimeaikaisesta edistymisestä tällä luokalla. JDK: n vakioluokka, joka antoi merkkijonon objekteista, vaikka objektien luokan määritelmät eivät tarjoaisikaan nimenomaista toString () olisi hyödyllistä.

Apache Commons ToStringBuilder voi olla suosituin ratkaisu turvallisten toString () -toteutusten rakentamiseen joillakin perusmuotoilun ohjaimilla. Olen aiemmin kirjoittanut blogia ToStringBuilderiin, ja ToStringBuilderin käyttöön liittyy lukuisia muita online-resursseja.

Glen McCluskeyn Java Technology Tech -vinkki "Writing toString Methods" sisältää lisätietoja hyvän toString () -metodin kirjoittamisesta. Eräässä lukijakommentissa Giovanni Pelosi mainitsee parempana delegoida ilmentymän merkkijonon tuotannon perintöhierarkioissa toString () tähän tarkoitukseen rakennetulle edustajaryhmälle.

Johtopäätös

Mielestäni useimmat Java-kehittäjät tunnustavat hyvän arvon toString () toteutukset. Valitettavasti nämä toteutukset eivät ole aina niin hyviä tai hyödyllisiä kuin voisivat olla. Tässä viestissä olen yrittänyt hahmotella joitain näkökohtia parantamiseksi toString () toteutukset. Vaikka a toString () menetelmä ei (tai ainakaan ei saisi) vaikuttaa logiikkaan kuten on yhtä suuri (objekti) tai hash koodin() menetelmä, se voi parantaa virheenkorjausta ja diagnostiikan tehokkuutta. Vähemmän aikaa, joka kuluu objektin tilan selvittämiseen, tarkoittaa enemmän aikaa ongelman korjaamiseen, kiinnostavampiin haasteisiin siirtymiseen ja asiakkaiden tarpeiden täyttämiseen.

Tämän tarinan "Java toString () näkökohdat" julkaisi alun perin JavaWorld.