Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Java Tip 99: Automate toString() creation

Exploit the power of Reflection and save significant coding time

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Developers working on large projects commonly spend hours writing useful toString methods. Even if each class doesn't get its own toString method, each data container class will. Allowing each developer to write toString his or her own way can lead to chaos; each developer will undoubtedly come up with a unique format. As a result, using the output during debugging becomes more difficult than necessary with no obvious benefit. Therefore, each project should standardize on a single format for toString methods and then automate their creation.

Automate toString

I will now demonstrate a utility with which you can do just that. This tool automatically generates a regular and robust toString method for a specified class, almost eliminating the time spent developing the method. It also centralizes the toString() format. If you change the format, you must regenerate the toString methods; however, this is still much easier than manually changing hundreds or thousands of classes.

Maintaining the generated code is easy, too. If you add more attributes in the classes, you may be required to make the changes in the toString method also. Since the generation of toString methods is automated, you need only run the utility on the class again to make your changes. This is simpler and less error-prone than the manual approach.

The code

This article is not meant to explain the Reflection API; the following code assumes that you have at least an understanding of the concepts behind Reflection. You can visit the Resources section for the Reflection API's documentation. The utility is written as follows:

package fareed.publications.utilities;
import java.lang.reflect.*;
public class ToStringGenerator
{
    public static void main(String[] args)
     {
       if (args.length == 0)
                {
                  System.out.println("Provide the class name as the command line argument");
                  System.exit(0);
                }
                      
                try {
                
                Class targetClass = Class.forName(args[0]);
                      
                if (!targetClass.isPrimitive() && targetClass != String.class)
                {
                        Field fields[] = targetClass.getDeclaredFields();
                                 
                        Class cSuper = targetClass.getSuperclass(); // Retrieving the super class 
                                            
                        output("StringBuffer buffer = new StringBuffer(500);"); // Buffer Construction 
                                 
                        if (cSuper != null && cSuper != Object.class) {
                          output("buffer.append(super.toString());"); // Super class's toString()
                         }
                                 
                        for (int j = 0; j < fields.length; j++) {
                          output("buffer.append(\"" + fields[j].getName() + " = \");"); // Append Field name
                          if (fields[j].getType().isPrimitive() || fields[j].getType() == String.class) // Check for a primitive or string             
                                output("buffer.append(this." + fields[j].getName() + ");"); // Append the primitive field value 
                          else
                             {
                              /* It is NOT a primitive field so this requires a check for the NULL value for the aggregated object */
                              output("if ( this." + fields[j].getName() + "!= null )" ); 
                              output("buffer.append(this." + fields[j].getName() + ".toString());");
                              output("else buffer.append(\"value is null\"); ");
                             } // end of else
                          } // end of for loop
                                 output("return  buffer.toString();");
                } 
               } catch (ClassNotFoundException e) {
                    System.out.println("Class not found in the class path");
                    System.exit(0);
              } 
       }
           
   private static void output(String data)
     {
        System.out.println(data);
     }
           
}


The code output channel

The format of the code also depends on your project tool requirements. Some developers may prefer to have the code in a user-defined file on disk. Other developers are satisfied with the system.out console, which allows them to copy and embed the code in the actual file manually. I simply leave those options to you and use the simplest method: system.out statements.

Limitations to the approach

There are two important limitations to this approach. The first is that it doesn't support objects containing cycles. If object A contains a reference to object B, which then contains a reference to object A, this tool won't work. However, that case will be rare for many projects.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources