toString
method is widely implemented. It provides a simple, convenient mechanism
for debugging classes during development.
It's also widely used for logging, and for passing informative error messages
to Exception
constructors and assertions. When used in these
informal ways, the exact format of toString
is not part of the
contract of the method, and callers should not rely on the exact format
of the returned String
.
The toString
method may occasionally be used more formally,
however. An example is a simple mechanism for translating an object into a
well-defined textual form (toString
) and back again (valueOf
).
In this case, it's particularly important to specify the exact form of such text in javadoc.
When implementing toString
, StringBuilder
can be used instead of the + concatenation operator, since the StringBuilder.append
operation is slightly faster.
Example 1
import java.util.*; import java.time.LocalDate; public final class Truck { /** Simple test harness. */ public static void main(String... args){ Truck truck = new Truck(); System.out.println(truck); } /** * Intended only for debugging. * * <P>Here, the contents of every field are placed into the result, with * one field per line. */ @Override public String toString() { StringBuilder result = new StringBuilder(); String NL = System.getProperty("line.separator"); result.append(this.getClass().getName() + " Object {" + NL); result.append(" Name: " + name + NL); result.append(" Number of doors: " + numDoors + NL); result.append(" Manufactured on: " + whenManufactured + NL ); result.append(" Color: " + color + NL); //Note that Collections and Maps also override toString result.append(" Options: " + options + NL); result.append("}"); return result.toString(); } //..other methods elided // PRIVATE // /** Some toy data for illustrating toString(). */ private String name = "Dodge"; private Integer numDoors = 2; private LocalDate whenManufactured = LocalDate.of(2007, 9, 1); private String color = "Black"; private List<String> options = List.of("Air Con", "Cruise Control"); }
Truck Object { Name: Dodge Number of doors: 2 Year manufactured: Wed Aug 29 15:49:10 ADT 2007 Color: Fuchsia Options: [Air Conditioning] }
Example 2
This implementation uses reflection to inspect
both field names and field values. Note that superclass fields do not
contribute to this implementation.
import java.util.*; import java.lang.reflect.Field; import java.time.LocalDate; public final class Van { /** * Build a Van object and display its textual representation. * * Note that the Collection classes have * their own implementation of <code>toString</code>, as exemplified here * by the List field holding the Options. */ public static void main (String... args) { List<String> options = new ArrayList<>(); options.add("Air Conditioning"); options.add("Leather Interior"); Van van = new Van("Dodge", 4, LocalDate.now(), "Blue", options); log(van); } public Van( String name, Integer numDoors, LocalDate whenManufactured, String color, List<String> options ){ this.name = name; this.numDoors = numDoors; this.whenManufactured = whenManufactured; this.color = color; this.options = options; } //..other methods elided /** * Intended only for debugging. * * <P>Here, a generic implementation uses reflection to print * names and values of all fields <em>declared in this class</em>. Note that * superclass fields are left out of this implementation. * * <p>The format of the presentation could be standardized by using * a MessageFormat object with a standard pattern. */ @Override public String toString() { StringBuilder result = new StringBuilder(); String NL = System.getProperty("line.separator"); result.append(this.getClass().getName()); result.append(" Object {"); result.append(NL); //determine fields declared in this class only (no fields of superclass) Field[] fields = this.getClass().getDeclaredFields(); //print field names paired with their values for (Field field : fields) { result.append(" "); try { result.append(field.getName()); result.append(": "); //requires access to private field: result.append(field.get(this)); } catch (IllegalAccessException ex) { log(ex.toString()); } result.append(NL); } result.append("}"); return result.toString(); } // PRIVATE private String name; private Integer numDoors; private LocalDate whenManufactured; private String color; private List<String> options; private static void log(Object thing) { System.out.println(thing.toString()); } }
Van Object {
fName: Dodge
fNumDoors: 4
fYearManufactured: Thu Sep 30 19:16:14 EDT 2004
fColor: Blue
fOptions: [Air Conditioning, Leather Interior]
}
Example 3
The WEB4J tool
provides a utility method for implementing toString
in a single
line of code. Its implementation uses reflection.
import java.math.BigDecimal; import hirondelle.web4j.model.ModelCtorException; import hirondelle.web4j.model.ModelUtil; import hirondelle.web4j.model.Id; import hirondelle.web4j.model.Check; import hirondelle.web4j.util.Util; import hirondelle.web4j.model.Validator; /** Model Object for a Restaurant. */ public final class Resto { /** * Full constructor. * * @param aId underlying database internal identifier (optional) 1..50 characters * @param aName of the restaurant (required), 2..50 characters * @param aLocation street address of the restaurant (optional), 2..50 characters * @param aPrice of the fish and chips meal (optional) $0.00..$100.00 * @param aComment on the restaurant in general (optional) 2..50 characters */ public Resto( Id aId, String aName, String aLocation, BigDecimal aPrice, String aComment ) throws ModelCtorException { fId = aId; fName = Util.trimPossiblyNull(aName); fLocation = Util.trimPossiblyNull(aLocation); fPrice = aPrice; fComment = Util.trimPossiblyNull(aComment); validateState(); } public Id getId() { return fId; } public String getName() { return fName; } public String getLocation() { return fLocation; } public BigDecimal getPrice() { return fPrice; } public String getComment() { return fComment; } /** Intended for debugging only. */ @Override public String toString() { return ModelUtil.toStringFor(this); } //..elided //PRIVATE// private final Id fId; private final String fName; private final String fLocation; private final BigDecimal fPrice; private final String fComment; //..elided }
hirondelle.fish.main.resto.Resto { Name: Cedars Eatery Location: Water and Prince Id: 6 Comment: Big portions Price: 7.89 }
QuoteField
is a type safe enumeration.
The return value of toString
is used in the normal operation of the program - not just
for logging.
Note that it forms a pair with the valueFrom(String)
method, since one formats
the object into a String
, and the other parses a String
into an object.
package hirondelle.stocks.table; /** * Enumeration for the fields of the * {@link hirondelle.stocks.quotes.Quote} class. * * Advantages to using this class as part of a table model : * <ul> * <li> can parse text which maps table columns to fields * <li> can be used for column names * <li> length of <tt>QuoteField.values()</tt> gives the column count * </ul> */ public enum QuoteField { Stock("Stock"), Price("Price"), Change("Change"), PercentChange("%Change"), Profit("Profit"), PercentProfit("%Profit"); /** * Return a text representation of the <tt>QuoteField</tt>. * * Return values : <tt>Stock, Price, Change, %Change, Profit, %Profit</tt>. * @return value contains only letters, and possibly a percent sign. */ @Override public String toString() { return fName; } /** * Parse text into a <tt>QuoteField</tt>. * * <P>The text is matched according to the value of {@link #toString()}, * not from the symbolic name of the enumerated item. */ public static QuoteField valueFrom(String aText){ for (QuoteField quoteField: values()){ if(quoteField.toString().equals(aText)) { return quoteField; } } throw new IllegalArgumentException("Cannot parse into a QuoteField: " + aText); } private final String fName; /** * @param aName only letters and percent sign are valid characters. */ private QuoteField(String aName) { fName = aName; } }