The usefulness of the Java toString() method is documented in Joshua Bloch's Effective Java (Item 10 in the Second Edition and Item 9 in the First Edition). The toString() method's usefulness is so proven that some languages newer than Java have taken the concept even further. Scala, for example, provides a type of class (case class) in which a reasonable default toString() is provided. ActionScript provides an ObjectUtil.toString() method that can be used to print an object's contents even when that object's class does not have its own toString implementation.
Unfortunately, toString can be an oft-forgotten method in a new class's implementation and, even when implemented, may suffer some implementation details that reduce its usefulness. These negative considerations include lack of maintenance of the toString() method (such as not adding important new data members of a class to it), lack of standard format or coverage of different data types (especially Collections), handling of nulls, and the sheer tedium associated with hand-writing toString() methods.
The vast Apache Commons project (formerly known as Jakarta Commons) is rich with useful libraries, but I think many of them are often unappreciated by many of us because we're unaware of them. While there have been several blog entries and articles on Apache Commons, I still find myself learning about some of the useful features available in the Apache Commons project (which is really a repository of multiple libraries that in many ways are not very related). Perhaps even more surprisingly, I find that even some relatively experienced Java developers who are familiar with some of the more common pieces of Apache Commons (such as Commons Logging) are not familiar with the ToStringBuilder in the Commons Lang component.
The beauty of the Commons ToStringBuilder is that it helps overcome many of the issues that have traditionally reduced the effectiveness of toString() method implementations. In the remainder of this blog entry, I intend to demonstrate how easy it is to apply ToStringBuilder to any Java class. I will do this using several simple classes that mostly consist of data members and toString() implementations using ToStringBuilder.
The first class with code listed here (ToStringDemonstrator) is the main class for running the examples that demonstrate ToStringBuilder in action. This class doesn't do much itself, but it does print out the contents of each object via the objects' toString() implementations.
ToStringDemonstrator.java
package dustin.examples.common;
/**
* This example class demonstrates the high utility of the Apache Commons
* ToStringBuilder class and is associated with a blog entry at
* http://marxsoftware.blogspot.com/.
*
* @author Dustin
*/
public class ToStringDemonstrator
{
/**
* Main executable for demonstrating Jakarta Commons ToStringBuilder.
*
* @param args The command line arguments; none expected.
*/
public static void main(String[] arguments)
{
final Parent parent = new Parent();
System.out.println("PARENT:\n" + parent + "\n");
final ReflectionChild reflectionChild = new ReflectionChild();
System.out.println("CHILD/REFLECTION:\n" + reflectionChild + "\n");
final ReflectionChildExcludes reflectionExcludesChild =
new ReflectionChildExcludes();
System.out.println(
"CHILD/REFLECTION/EXCLUDES:\n" + reflectionExcludesChild + "\n");
final TransientsChild transientsChild = new TransientsChild();
System.out.println("CHILD/TRANSIENTS:\n" + transientsChild + "\n");
final ExplicitChild explicitChild = new ExplicitChild();
System.out.println("CHILD/EXPLICIT:\n" + explicitChild + "\n");
final StyleChild styleChild = new StyleChild();
System.out.println("CHILD/STYLE:\n" + styleChild + "\n");
}
}
The main executable class whose code is shown above instantiates several objects and implicitly calls their toString() implementations via System.out calls on those instantiated objects. The objects include one parent class and a bunch of its child classes. The inheritance hierarchy helps demonstrate ToStringBuilder's treatment of parent data members in child toString() implementations.
The output of each class's toString() implementation demonstrates a different characteristic of ToStringBuilder. I will go through the code for each object along with the String representation of each class generated by ToStringBuilder.
Parent.java
This class serves as a parent class for the rest of the classes in the example. It also demonstrates ToStringBuilder.reflectionToString(Object). This method allows one to take advantage of reflection and, in a single line of code, return the String representation of the class's data members. In this example, the object's this reference is passed to the method so that the same object's String representation is generated via reflection. Note, however, that this could be used on a separate class instance as well if that instance was significant to this class but did not have its own useful toString() implementation.
This class intentionally includes many different attributes of different types and with different modifiers (such as static and transient) to more fully demonstrate default behavior and custom behavior of the ToStringBuilder and related classes.
package dustin.examples.common;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* An example class to which Apache Commons ToStringBuilder can be applied.
* This specific example demonstrates using the simple
* ToStringBuilder.reflectionToString(object) approach without any non-default
* toString styles being provided and without any fields that should not be
* part of the toString() being specified.
*
* @author Dustin
*/
public class Parent
{
/** String member that is private. */
private String parentString = "Hello Mars!";
/** Primitive (int) member that is private. */
private int parentInt = 12;
/** Enum (since JDK 1.5) type. */
protected enum FruitEnum
{
APPLE,
BANANA,
MANGO,
ORANGE,
PINEAPPLE,
WATERMELON
};
/** Enum member. */
protected FruitEnum fruit = FruitEnum.MANGO;
/** Array (of Enums) member. */
protected FruitEnum[] fruits = { FruitEnum.APPLE, FruitEnum.PINEAPPLE };
/** Null Array. */
protected String[] nullArray = null;
/** List (of Enums) member. */
protected List<FruitEnum> fruitsList = new ArrayList<FruitEnum>();
/** Null List. */
protected List<String> nullList = null;
/** Map (of Enums) member. */
protected Map<FruitEnum,String> fruitsMap =
new EnumMap<FruitEnum,String>(FruitEnum.class);
/** Null value for data member. */
protected String nullString = null;
/** Transient data member. */
protected transient String transientString = "Transient!";
/** Static data member. */
protected static int staticCount = 0;
/** No-arguments constructor. */
public Parent()
{
fruitsList.add(FruitEnum.BANANA);
fruitsList.add(FruitEnum.ORANGE);
fruitsMap.put(FruitEnum.APPLE, "One a day keeps the doctor away.");
fruitsMap.put(FruitEnum.MANGO, "Paulie really likes these.");
fruitsMap.put(FruitEnum.PINEAPPLE,
"Crossbreeding of an apple tree and a pine tree?");
}
/**
* The most important method of this class because this entire example is on
* ToStringBuilder.
*
* @return The String representation of this class as constructed via
* reflection by Apache Commons ToStringBuilder.
*/
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
}
The output of this class's toString method is shown next:
PARENT:
dustin.examples.common.Parent@10b30a7[parentString=Hello Mars!,parentInt=12,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>]
The generated output demonstrates how easy it was, with one line of code, to represent much of the object's state via its toString() method. The generated output includes handling of an array, a List, Map, and even a null value. The example also demonstrates that the default setting of ToStringBuilder does not include statics or transients.
ReflectionChild.java
This class extends the Parent class shown above. This example will demonstrate ToStringBuilder's handling of inherited data, including of the parent class's private data.
package dustin.examples.common;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* This class demonstrates Apache Commons ToStringBuilder behavior in an
* inheritance/extends relationship.
*
* @author Dustin
*/
public class ReflectionChild extends Parent
{
private String childString = "Hello Jupiter!";
/** No-arguments constructor. */
public ReflectionChild() {}
/**
* Demonstrating automatically generated (via reflection) toString() of a
* child class using ToStringBuilder (Apache Commons).
*
* @return String constructed from this object via reflection by the Apache
* Commons ToStringBuilder class.
*/
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
}
As with the Parent.toString() implementation, this implementation uses the simple, default ToStringBuilder.reflectionToString method on itself (this). The output from running this class's toString() is shown next:
CHILD/REFLECTION:
dustin.examples.common.ReflectionChild@66848c[childString=Hello Jupiter!,parentString=Hello Mars!,parentInt=12,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>]
One of the most interesting aspects of this child's toString() output is that even the parent's private data members (privateInt and privateString) are included in the ToStringBuilder-generated String. It is convenient that the child class, with the same single line of code, can provide a String representation of its content (including what it inherits from its parent).
ReflectionChildExcludes.java
There are times when we don't want every member to be included in the generated String representation of the object contents. As shown above, one way to prevent this is to make the data member static or transient. However, it is not really a good idea to make a data member transient or static simply for its toString() implementation. Fortunately, the ReflectionToStringBuilder class (extends ToStringBuilder) provides an easy mechanism for excluding fields that you don't want included in the toString() generation. In this case, I am not comfortable having a child class display its parent's private data as though it was the child's data. I can exclude a private element (or any other desired field) with the ReflectionToStringBuilder.setExcludeFieldNames method. In this case, I'll exclude the parent's two private data members (privateInt and privateString) and a data member from this same class (ignoreString) that I don't want included in the String representation.
This example takes ReflectionToStringBuilder even further than simply excluding certain fields from the generated String representation. The example also uses other methods on ReflectionToStringBuilder to specify that static members should be included and that transient members should be included.
package dustin.examples.common;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
/**
* This class demonstrates use of the reflective capabilities of Apache Commons
* ToStringBuilder with data members not to be included in the toString()
* specified.
*
* @author Dustin
*/
public class ReflectionChildExcludes extends Parent
{
/** String defined for this child. */
private String childString = "Hello Pluto!"; // still a planet in this example
/** Child's static member. */
private static String staticChildString = "I am static!";
private transient String transientChildString = "I am transient!";
/** String to be ignored in toString() construction. */
private String ignoreString = "Ignore Me!";
/** String to not be ignored. */
private String doNotIgnoreString = "Do not ignore me!";
/**
* Uses Apache Commons ReflectionToStringBuilder to generate String
* representation of me.
*
* @return String representation of me.
*/
@Override
public String toString()
{
final ReflectionToStringBuilder reflectionToStringBuilder =
new ReflectionToStringBuilder(this);
reflectionToStringBuilder.setAppendStatics(true);
reflectionToStringBuilder.setAppendTransients(true);
reflectionToStringBuilder.setExcludeFieldNames(
new String[] {"parentString", "parentInt", "ignoreString"});
return reflectionToStringBuilder.toString();
}
}
The output of this code shows that the specified fields are not included in the output and that the static and transient fields are included in the output.
CHILD/REFLECTION/EXCLUDES:
dustin.examples.common.ReflectionChildExcludes@e09713[childString=Hello Pluto!,staticChildString=I am static!,transientChildString=I am transient!,doNotIgnoreString=Do not ignore me!,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>,transientString=Transient!,staticCount=0]
TransientsChild.java
This example demonstrates how to explicitly specify that transient data members should be included in the String representation using simple ToStringBuilder.reflectionToString(Object,ToStringStyle,boolean) rather than ReflectionToStringBuilder. In this example, the default style is still being used, so null is passed in as the second parameter. The 'true' passed as the third parameter indicates that transient data members should be included in the generated String representation of this object.
package dustin.examples.common;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* Class demonstrating Apache Commons ToStringBuilder's explicit inclusion of
* transient data members in the generated String.
*
* @author Dustin
*/
public class TransientsChild extends Parent
{
/** Transient member of this child class. */
private transient String childString = "Hello Neptune!";
/** No-arguments constructor. */
public TransientsChild() {}
/**
* Use Apache Commons ToStringBuilder to generate a String representation of
* me that includes transient data members.
*
* @return String representation of me.
*/
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this, null, true);
}
}
The output of this code is shown next.
CHILD/TRANSIENTS:
dustin.examples.common.TransientsChild@e0e1c6[childString=Hello Neptune!,parentString=Hello Mars!,parentInt=12,fruit=MANGO,fruits={APPLE,PINEAPPLE},nullArray=<null>,fruitsList=[BANANA, ORANGE],nullList=<null>,fruitsMap={APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>,transientString=Transient!]
This output shows that this object's transient members (including those inherited from its parent) are included in the String representation that is generated.
ExplicitChild.java
All of the examples so far have used reflection to generate the String representation of the object's state. The reflection approach is convenient and offers the significant advantage of reflecting the current state of the object even if a developer adding a new data member to the class definition forgets to add it to the toString() implementation.
However, there are also reasons that reflection may not be the preferred approach for implementation a toString() method. In such cases, ToStringBuilder supports building a String representation with explicit specification of which fields to include in that representation. This is similar to hand-writing the toString() implementation, but offers the advantage of handling arrays, collections, nulls, and other special circumstances implicitly.
package dustin.examples.common;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* This class demonstrates use of ToStringBuilder's append method to append
* only explicitly specified data members of this class to the class's toString()
* implementation.
*
* @author Dustin
*/
public class ExplicitChild extends Parent
{
private String childString = "Hello Venus!";
public ExplicitChild() {}
/**
* Users Apache Commons ToStringBuilder to construct a String representation
* of me.
*
* @return String representation of me constructed via Apache Commons
* ToStringBuilder.
*/
@Override
public String toString()
{
return new ToStringBuilder(this)
.append("childString", this.childString)
.append("fruitsList", this.fruitsList)
.append(this.fruitsMap) // member name intentionally NOT specified
.append("nullString", this.nullString)
.toString();
}
}
The code above explicitly includes four data members in its generated String representation. One of them is intentionally included without the field name. The output is shown next:
CHILD/EXPLICIT:
dustin.examples.common.ExplicitChild@1389e4[childString=Hello Venus!,fruitsList=[BANANA, ORANGE],{APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?},nullString=<null>]
Only the explicitly specified data members are included in the String representation and the fruitsMap does not have a field name included.
StyleChild.java
The ToStringBuilder and ReflectionToStringBuilder provide many benefits including the ability to take advantage of reflection to make a change-resistance toString() implementation and the advantage of automatic handling of special circumstances like Collections, arrays, null values, etc. However, all examples so far have used a default style and there may be reasons for a different toString() style. For example, coding conventions may require a different toString() style. ToStringBuilder offers some easy ways to customize style used in the String representations it generates. The next example indicates how easy it is to adjust several key aspects of a String representation using StandardToStringStyle, a concrete extension of the abstract ToStringStyle class.
package dustin.examples.common;
import org.apache.commons.lang.builder.StandardToStringStyle;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
/**
* This class demonstrates use of Apache Commons ToStringBuilder with use of
* non-default styles.
*
* @author Dustin
*/
public class StyleChild extends Parent
{
/**
* String representation of me generated with Apache Commons ToStringBuilder
* using non-default toString style.
*
* @return String representation of me.
*/
@Override
public String toString()
{
final StandardToStringStyle style = new StandardToStringStyle();
style.setNullText("---NULL---");
style.setFieldSeparator(";");
style.setArrayStart("{{{");
style.setArrayEnd("}}}");
style.setArraySeparator("|");
style.setFieldNameValueSeparator(":");
return ToStringBuilder.reflectionToString(this, style);
}
}
Previous examples demonstrated that the default ToStringBuilder style included null representation as <null>, used comma (,) to separate fields, used used a single curly brace ({ and }) to demarcate arrays, used comma to separate elements in an array, and used equals sign (=) to tie a field name to its value. In this example, those settings have all been overridden and the output of the example demonstrates this.
CHILD/STYLE:
dustin.examples.common.StyleChild@157f0dc[parentString:Hello Mars!;parentInt:12;fruit:MANGO;fruits:{{{APPLE|PINEAPPLE}}};nullArray:---NULL---;fruitsList:[BANANA, ORANGE];nullList:---NULL---;fruitsMap:{APPLE=One a day keeps the doctor away., MANGO=Paulie really likes these., PINEAPPLE=Crossbreeding of an apple tree and a pine tree?};nullString:---NULL---]
As the output above demonstrates, the new style has been easily applied to the String representation generated by the Apache Commons ToStringBuilder.
Performance Considerations
UPDATE (2 November 2008): This entire paragraph on "Performance Considerations" has been added since the original posting of this blog. As detailed in the Reflection API Trail of the Java Tutorial, the benefits of reflection do not come without drawbacks. One of the primary drawbacks is the reflection performance cost. The Javadoc documentation for ToStringBuilder (2.4) talks about the performance limitations of the reflective approach and also mentions problems associated with using the reflecting approach with a security manager. Please see feedback comments from Santhosh and davidecr for details regarding their previous experience with performance penalties associated with using ToStringBuilder's reflection capabilities.
Conclusion
This blog entry has focused on Apache Commons Lang component's ToStringBuilder and related classes. Even the several examples used in this blog entry have not fully demonstrated all of the features of ToStringBuilder and ReflectionToStringBuilder.
There are other ways to build a String representation of an object without hand-writing it or using the Apache Commons ToStringBuilder. For example, Dmitriy Setrakyan has written on JavaLobby about GridGain's Refactor-Safe ToStringBuilder. The Eclipse IDE has a toString generation plug-in and smart-codegen for NetBeans/Maven includes a toString generator.
The lang.builder.* package contains many other builder classes for methods that are common to many Java classes. These builders include HashCodeBuilder, EqualsBuilder, and CompareToBuilder. On top of these builders, Commons Lang also provides numerous other classes that make working with the JDK classes easier.
Additional Resources on Apache Commons
* Using the Jakarta Commons, Part 1
* Using the Jakarta Commons, Part 2
* Using the Jakarta Commons, Part 3
* Extend the JDK Classes with Jakarta Commons, Part 1
* Extend the JDK Classes with Jakarta Commons, Part 2
* Extend the JDK Classes with Jakarta Commons, Part 3
5 comments:
Dustin,
Nice article about ToStringBuilder. There is one drawback of using reflection method on high-used objects is the performance. I did some runs with reflection method and using standard StringBuilder(or Buffer) and found that Reflection method is around 4 to 5 times slower over traditional method of toString.
However this performance trades with convenience of single line of code to add toString and to be considered based on the object usage.
Thanks
nice and detailed article, while reading it I remember a couple of years ago when I used to code with the reflection mechanism to build the toString representation, this caused me a big headache related to it's use in a swing application, you have to be very careful about it in swing because sometimes the toString method is being called from some classes (components) by default, and the painting mechanism will be slowed down.
Santhosh and davidecr,
Thanks for the comments on the performance cost of using reflection. Whenever I consider reflection for anything, a potential red flag goes up in my mind regarding performance. I appreciate you pointing out your specific experiences with this performance impact. Both of your comments add significant value to the discussion and are a good reminder about using reflection here very carefully. In fact, I had intended to make a passing remark about potential reflection impacts on performance, but forgot to do so. Fortunately, your comments cover it well.
To have a better view of the styles available with ToStringBuilder find here an interesting article on the formatting
Sebastien,
The ToStringBuilder Style post is handy for quickly illustrating setting of various styles with ToStringBuilder.
Dustin
Post a Comment