Programmering

Hensyn til Java toString ()

Selv Java-utviklere som begynner, er klar over nytten av Object.toString () -metoden som er tilgjengelig for alle forekomster av Java-klasser, og som kan overstyres for å gi nyttige detaljer angående en bestemt forekomst av en Java-klasse. Dessverre, til og med erfarne Java-utviklere utnytter av og til ikke fullt ut denne kraftige Java-funksjonen av en rekke årsaker. I dette blogginnlegget ser jeg på ydmyke Java toString () og beskrive enkle grep man kan ta for å forbedre utilitarismen til toString ().

Implisitt implementere (tilsidesette) toString ()

Kanskje det viktigste hensynet knyttet til å oppnå maksimal verdi fra toString () er å gi implementeringer av dem. Selv om roten til alle Java-klassehierarkier, Object, gir en toString () -implementering som er tilgjengelig for alle Java-klasser, er denne metoden som standard standard atferd aldri nyttig. Javadoc for Object.toString () forklarer hva som leveres som standard for toString () når en tilpasset versjon ikke er gitt for en klasse:

ToString-metoden for klasse Object returnerer en streng som består av navnet på klassen som objektet er en forekomst av, tegnet tegnet `@ 'og den usignerte heksadesimale representasjonen av hash-koden til objektet. Med andre ord returnerer denne metoden en streng som er lik verdien av:getClass (). getName () + '@' + Integer.toHexString (hashCode ())

Det er vanskelig å komme opp i en situasjon der navnet på klassen og den heksadesimale representasjonen av objektets hash-kode atskilt med @ -tegnet er nyttig. I nesten alle tilfeller er det betydelig mer nyttig å gi en tilpasset, eksplisitt

toString ()

implementering i en klasse for å overstyre denne standardversjonen.

Javadoc for Object.toString () forteller oss også hva en toString () implementering generelt bør innebære og gir også den samme anbefalingen jeg gir her: overstyre toString ():

Generelt setttoString metoden returnerer en streng som "tekstlig representerer" dette objektet. Resultatet skal være en kortfattet, men informativ fremstilling som er lett for en person å lese. Det anbefales at alle underklasser overstyrer denne metoden.

Hver gang jeg skriver en ny klasse, er det flere metoder jeg vurderer å legge til som en del av handlingen med å opprette ny klasse. Disse inkluderer

hashCode ()

og

er lik (Objekt)

hvis det passer. Imidlertid, etter min erfaring og etter min mening, implementering eksplisitt

toString ()

er alltid passende.

Hvis Javadocs "anbefaling" om at "alle underklasser overstyrer denne metoden" ikke er nok (så antar jeg ikke at min anbefaling er det heller) for å rettferdiggjøre for en Java-utvikler viktigheten og verdien av en eksplisitt toString () metode, så anbefaler jeg å gå gjennom Josh Blochs effektive Java-element "Overstyr alltid til streng" for ytterligere bakgrunn om viktigheten av å implementere toString (). Det er min mening at alle Java-utviklere bør eie en kopi av Effektiv Java, men heldigvis kapittelet med dette elementet på toString () er tilgjengelig for de som ikke eier en kopi: Methods Common to All Objects.

Vedlikehold / oppdater toString ()

Det er frustrerende å eksplisitt eller implisitt kalle et objekt toString () i en loggerklæring eller et annet diagnostisk verktøy og har standard klassenavn og objektets heksidecimale hash-kode returnert i stedet for noe mer nyttig og lesbart. Det er nesten like frustrerende å ha en ufullstendig toString () implementering som ikke inkluderer vesentlige deler av objektets nåværende egenskaper og tilstand. Jeg prøver å være disiplinert nok og generere og følge vanen med alltid å gjennomgå toString () implementering sammen med gjennomgang av er lik (Objekt) og hashCode () implementeringer av hvilken som helst klasse jeg jobber med som er ny for meg, eller når jeg legger til eller endrer attributtene til en klasse.

Bare fakta (men alle / det meste av dem!)

I kapitlet om Effektiv Java tidligere nevnt, skriver Bloch: "Når det er praktisk, bør toString-metoden returnere all interessant informasjon som finnes i objektet." Det kan være vondt og kjedelig å legge til alle attributtene til en attributt-tung klasse til implementeringen av toString, men verdien for de som prøver å feilsøke og diagnostisere problemer knyttet til den klassen, vil være verdt innsatsen. Jeg prøver vanligvis å ha alle viktige ikke-null-attributter for min forekomst i den genererte strengrepresentasjonen (og noen ganger inkluderer det faktum at noen attributter er null). Jeg legger også vanligvis til minimal identifiserende tekst for attributtene. Det er på mange måter mer en kunst enn en vitenskap, men jeg prøver å inkludere nok tekst til å skille attributter uten å blundge fremtidige utviklere med for mye detalj. Det viktigste for meg er å få attributtenes verdier og en eller annen type identifiseringsnøkkel på plass.

Kjenn publikum

En av de vanligste feilene jeg har sett ikke-nybegynnere Java-utviklere gjør med hensyn til toString () er å glemme hva og hvem toString () er vanligvis ment for. Generelt, toString () er et diagnostisk og feilsøkingsverktøy som gjør det enkelt å logge detaljer om en bestemt forekomst på et bestemt tidspunkt for senere feilsøking og diagnostikk. Det er vanligvis en feil å la brukergrensesnittene vise strengrepresentasjoner generert av toString () eller å ta logiske avgjørelser basert på en toString () representasjon (faktisk er det skjøre å ta logiske avgjørelser på en hvilken som helst streng!). Jeg har sett velmenende utviklere ha toString () returnere XML-format for bruk i et annet XML-vennlig aspekt av koden. En annen vesentlig feil er å tvinge klienter til å analysere strengen som returneres fra toString () for å programmatisk få tilgang til datamedlemmer. Det er sannsynligvis bedre å tilby en offentlig getter / accessor-metode enn å stole på toString () aldri forandret seg. Alt dette er feil fordi disse tilnærmingene glemmer intensjonen om a toString () gjennomføring. Dette er spesielt snikende hvis utvikleren fjerner viktige egenskaper fra toString () metode (se siste element) for å få det til å se bedre ut i et brukergrensesnitt.

jeg liker toString () implementeringer for å ha alle relevante detaljer og for å gi minimal formatering for å gjøre disse detaljene mer velsmakende. Denne formateringen kan omfatte veloverveide valgte nye tegn [System.getProperty ("line.seperator");] og faner, kolon, semikolon osv. Jeg investerer ikke like mye tid som jeg ville i et resultat som ble presentert for en sluttbruker av programvaren, men jeg prøver å gjøre formateringen fin nok til å være mer leselig. Jeg prøver å implementere toString () metoder som ikke er for kompliserte eller dyre å vedlikeholde, men som gir noen veldig enkel formatering. Jeg prøver å behandle fremtidige vedlikeholdere av koden min slik jeg ønsker å bli behandlet av utviklere hvis kode jeg en dag vil vedlikeholde.

I varen hans på toString () implementering, sier Bloch at en utvikler skal velge om den skal ha eller ikke toString () returnere et bestemt format. Hvis et bestemt format er ment, bør det dokumenteres i Javadoc-kommentarene, og Bloch anbefaler videre at det gis en statisk initialisering som kan returnere objektet til dets forekomstskarakteristikker basert på en streng generert av toString (). Jeg er enig i alt dette, men jeg tror dette er mer trøbbel enn de fleste utviklere er villige til å gå. Bloch påpeker også at eventuelle endringer i dette formatet i fremtidige utgivelser vil forårsake smerte og angst for folk avhengig av det (det er derfor jeg ikke synes det er en god idé å ha logikk avhengig av en toString () produksjon). Med betydelig disiplin for å skrive og vedlikeholde passende dokumentasjon, med et forhåndsdefinert format for en toString () kan være plausibelt. Imidlertid virker det som problemer for meg og bedre å bare lage en ny og separat metode for slike bruksområder og forlate toString () ubelastet.

Ingen tolererte bivirkninger

Like viktig som toString () implementering er, det er generelt uakseptabelt (og absolutt ansett som dårlig form) å ha eksplisitt eller implisitt kall toString () påvirke logikken eller føre til unntak eller logiske problemer. Forfatteren av en toString () metoden bør være forsiktig med å sikre at referanser blir sjekket for null før du får tilgang til dem for å unngå NullPointerException. Mange av taktikkene jeg beskrev i innlegget Effektiv Java NullPointerException Handling kan brukes i toString () gjennomføring. For eksempel gir String.valueOf (Object) en enkel mekanisme for null sikkerhet på attributter av tvilsom opprinnelse.

Det er like viktig for toString () utvikler for å sjekke matrisestørrelser og andre størrelser på samlingen før han prøver å få tilgang til elementer utenfor samlingen Spesielt er det altfor lett å kjøre inn i et StringIndexOutOfBoundsException når du prøver å manipulere strengverdier med String.substring.

Fordi et objekt er toString () implementering kan lett påberopes uten at utvikleren samvittighetsfullt innser det, dette rådet for å sikre at det ikke kaster unntak eller utfører logikk (spesielt tilstandsendrende logikk) er spesielt viktig. Det siste noen ønsker er å få lov til å logge en forekomsts nåværende tilstand føre til et unntak eller endring i tilstand og atferd. EN toString () implementering bør effektivt være en skrivebeskyttet operasjon der objekttilstand leses for å generere en streng for retur. Hvis noen attributter endres i prosessen, er det sannsynlig at dårlige ting vil skje på uforutsigbare tider.

Det er min posisjon at a toString () implementering skal bare inkludere tilstand i den genererte strengen som er tilgjengelig i samme prosessrom på tidspunktet for genereringen. For meg er det ikke forsvarlig å ha en toString () implementering få tilgang til eksterne tjenester for å bygge opp en forekomsts streng. Kanskje litt mindre åpenbart er at en forekomst ikke skal fylle ut dataattributter fordi toString () ble kalt. De toString () implementering skal bare rapportere om hvordan ting er i dagens tilfelle og ikke om hvordan de kan være eller vil være i fremtiden hvis visse forskjellige scenarier oppstår eller hvis ting er lastet. For å være effektiv i feilsøking og diagnostikk, er toString () må vise hvordan forholdene er og ikke hvordan de kan være.

Enkel formatering blir hjertelig verdsatt

Som beskrevet ovenfor kan fordøyelig bruk av linjeseparatorer og faner være nyttig for å gjøre lange og komplekse forekomster mer velsmakende når de genereres i strengformat. Det er andre "triks" som kan gjøre ting finere. Ikke bare gjør det String.valueOf (Object) gi noen null beskyttelse, men det presenterer også null som String "null" (som ofte er den foretrukne representasjonen av null i en toString () -generert String. Arrays.toString (Object) er nyttig for å enkelt representere arrays som strenger (se innlegget mitt Stringifying Java Arrays for ytterligere detaljer).

Inkluder klassenavn i toString-representasjon

Som beskrevet ovenfor, er standardimplementeringen av toString () gir klassenavnet som en del av forekomstens representasjon. Når vi eksplisitt overstyrer dette, mister vi potensielt dette klassenavnet. Dette er ikke så farlig, vanligvis hvis man logger en streng for en forekomst fordi loggingsrammeverket inkluderer klassenavn. Imidlertid foretrekker jeg å være på den sikre siden og alltid ha kursnavnet tilgjengelig. Jeg bryr meg ikke om å holde den heksadesimale hashkodepresentasjonen fra standard toString (), men klassenavn kan være nyttig. Et godt eksempel på dette er Throwable.toString (). Jeg foretrekker å bruke den metoden i stedet for getMessage eller getLocalizedMessage fordi den tidligere (toString ()) inkluderer Kastbarklassens navn mens de to sistnevnte metodene ikke gjør det.

toString () Alternativer

Vi har for øyeblikket ikke dette (i det minste ikke en standardtilnærming), men det har vært snakk om en Objects-klasse i Java som vil gå langt mot å trygt og nyttig forberede strengrepresentasjoner av forskjellige objekter, datastrukturer og samlinger. Jeg har ikke hørt om noen nylig fremgang i JDK7 på denne klassen. En standardklasse i JDK som ga strengrepresentasjon av objekter selv når objektenes klassedefinisjoner ikke ga eksplisitt toString () ville være nyttig.

Apache Commons ToStringBuilder kan være den mest populære løsningen for å bygge sikre toString () -implementeringer med noen grunnleggende formateringskontroller. Jeg har blogget på ToStringBuilder tidligere, og det er mange andre online ressurser angående bruk av ToStringBuilder.

Glen McCluskeys Java Technology Tech Tip "Writing toString Methods" gir ytterligere detaljer om hvordan du skriver en god toString () -metode. I en av leserkommentarene uttaler Giovanni Pelosi en preferanse for å delegere produksjon av en strengrepresentasjon av en forekomst i arvshierarkier fra toString () til en delegatklasse bygget for det formålet.

Konklusjon

Jeg tror de fleste Java-utviklere anerkjenner verdien av godt toString () implementeringer. Dessverre er disse implementeringene ikke alltid så gode eller nyttige som de kunne være. I dette innlegget har jeg forsøkt å skissere noen hensyn til forbedring toString () implementeringer. Selv om en toString () metoden vil ikke (eller i det minste ikke) påvirke logikken som en er lik (Objekt) eller hashCode () metoden kan, kan den forbedre feilsøking og diagnostisk effektivitet. Mindre tid brukt på å finne ut hva objektets tilstand er, betyr mer tid på å løse problemet, gå videre til mer interessante utfordringer og tilfredsstille flere kundebehov.

Denne historien, "Java toString () Considerations" ble opprinnelig utgitt av JavaWorld.

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