61

I was writing a toString() for a class in Java the other day by manually writing out each element of the class to a String and it occurred to me that using reflection it might be possible to create a generic toString() method that could work on ALL classes. I.E. it would figure out the field names and values and send them out to a String.

Getting the field names is fairly simple, here is what a co-worker came up with:

public static List initFieldArray(String className) throws ClassNotFoundException {

    Class c = Class.forName(className);
    Field field[] = c.getFields();
    List<String> classFields = new ArrayList(field.length);

    for (int i = 0; i < field.length; i++) {
        String cf = field[i].toString();
        classFields.add(cf.substring(cf.lastIndexOf(".") + 1));
    }

    return classFields;
}

Using a factory I could reduce the performance overhead by storing the fields once, the first time the toString() is called. However finding the values could be a lot more expensive.

Due to the performance of reflection this may be more hypothetical then practical. But I am interested in the idea of reflection and how I can use it to improve my everyday programming.

James McMahon
  • 48,506
  • 64
  • 207
  • 283
  • From the jdk api "The result should be a concise but informative representation that is easy for a person to read." - an automatically generated value is a nice approximation, but sometimes you just have to bite the bullet and write your own. – wds Jan 14 '09 at 17:51
  • See also [Auto-generating toString Method](http://stackoverflow.com/questions/2653268/auto-generating-tostring-method) – Vadzim Dec 06 '13 at 18:34
  • possible duplicate of [java toString for any object](http://stackoverflow.com/questions/6807771/java-tostring-for-any-object) – msangel Aug 02 '15 at 22:10
  • @msangel this question predates that question by two years. Also this question is different in the sense that he isn't specifically asking about reflection. – James McMahon Aug 03 '15 at 21:43
  • Correction, missed his point about reflection. I think that is actually a duplicate of this question. – James McMahon Aug 03 '15 at 21:54

7 Answers7

108

Apache commons-lang ReflectionToStringBuilder does this for you.

import org.apache.commons.lang3.builder.ReflectionToStringBuilder

// your code goes here

public String toString() {
   return ReflectionToStringBuilder.toString(this);
}
hcpl
  • 17,382
  • 7
  • 72
  • 73
krosenvold
  • 75,535
  • 32
  • 152
  • 208
  • 3
    Yeah I actually saw that right after I posted this question. I am still interested in the details. I am going to dig through their code and how they implement it. – James McMahon Jan 14 '09 at 17:06
  • 9
    +1. When using this, understand that reflection is an order of magnitude slower than explicit field access. In some cases, particularly with objects that are still in development and subject to change, reflection is a good idea. Otherwise, it's probably better to use the explicit ToStringBuilder – kdgregory Jan 14 '09 at 18:47
11

Another option, if you are ok with JSON, is Google's GSON library.

public String toString() {
    return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}

It's going to do the reflection for you. This produces a nice, easy to read JSON file. Easy-to-read being relative, non tech folks might find the JSON intimidating.

You could make the GSONBuilder a member variable too, if you don't want to new it up every time.

If you have data that can't be printed (like a stream) or data you just don't want to print, you can just add @Expose tags to the attributes you want to print and then use the following line.

 new GsonBuilder()
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.create()
.toJson(this);
Daniel Schmidt
  • 401
  • 5
  • 5
6

W/reflection, as I hadn't been aware of the apache library:

(be aware that if you do this you'll probably need to deal with subobjects and make sure they print properly - in particular, arrays won't show you anything useful)

@Override
public String toString()
{
    StringBuilder b = new StringBuilder("[");
    for (Field f : getClass().getFields())
    {
        if (!isStaticField(f))
        {
            try
            {
                b.append(f.getName() + "=" + f.get(this) + " ");
            } catch (IllegalAccessException e)
            {
                // pass, don't print
            }
        }
    }
    b.append(']');
    return b.toString();
}


private boolean isStaticField(Field f)
{
    return Modifier.isStatic(f.getModifiers());
}
Steve B.
  • 55,454
  • 12
  • 93
  • 132
5

You can use already implemented libraries, as ReflectionToStringBuilder from Apache commons-lang. As was mentioned.

Or write smt similar by yourself with reflection API.

Here is some example:

class UniversalAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * Converts an object to a string representation that lists all fields.
    * @param obj an object
    * @return a string with the object's class name and all field names and
    * values
    */
   public String toString(Object obj) {
      if (obj == null) return "null";
      if (visited.contains(obj)) return "...";
      visited.add(obj);
      Class cl = obj.getClass();
      if (cl == String.class) return (String) obj;
      if (cl.isArray()) {
         String r = cl.getComponentType() + "[]{";
         for (int i = 0; i < Array.getLength(obj); i++) {
            if (i > 0) r += ",";
            Object val = Array.get(obj, i);
            if (cl.getComponentType().isPrimitive()) r += val;
            else r += toString(val);
         }
         return r + "}";
      }

      String r = cl.getName();
      // inspect the fields of this class and all superclasses
      do {
         r += "[";
         Field[] fields = cl.getDeclaredFields();
         AccessibleObject.setAccessible(fields, true);
         // get the names and values of all fields
         for (Field f : fields) {
            if (!Modifier.isStatic(f.getModifiers())) {
               if (!r.endsWith("[")) r += ",";
               r += f.getName() + "=";
               try {
                  Class t = f.getType();
                  Object val = f.get(obj);
                  if (t.isPrimitive()) r += val;
                  else r += toString(val);
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         }
         r += "]";
         cl = cl.getSuperclass();
      } while (cl != null);

      return r;
   }    
}
catch23
  • 17,519
  • 42
  • 144
  • 217
  • 1
    Glad someone provided a custom solution that I could tweak. None of the OTB solutions will print field class names or modifiers. – TastyWheat Jun 14 '19 at 15:14
5

If you're using Eclipse, you may also have a look at JUtils toString generator, which does it statically (generating the method in your source code).

Olivier
  • 1,232
  • 6
  • 10
4

Not reflection, but I had a look at generating the toString method (along with equals/hashCode) as a post-compilation step using bytecode manipulation. Results were mixed.

McDowell
  • 107,573
  • 31
  • 204
  • 267
  • 2
    That looks pretty interesting, I will have to read through it when I get a chance. That seems like it might be a really good approach. Alittle much for my particular domain, business apps, but if some could get your approach working without a hitch it would make a good JSR. – James McMahon Jan 14 '09 at 19:09
2

Here is the Netbeans equivalent to Olivier's answer; smart-codegen plugin for Netbeans.

Community
  • 1
  • 1
James McMahon
  • 48,506
  • 64
  • 207
  • 283