15

I have a lot of data objects, and I want to be able to generate a String representing each object, without implementing a toString method for each one.

I'm thinking of reflection for getting the fields and its values.

any other ideas?

thanks.

Kevin
  • 4,618
  • 3
  • 38
  • 61
AAaa
  • 3,659
  • 7
  • 35
  • 40
  • What kind of information (how much detail) do you want to see in toString() ? – user802421 Jul 24 '11 at 15:39
  • i just need the fields and the values but i have maybe 50 objects and i dont want to implement toString for each one of them. – AAaa Jul 24 '11 at 15:40
  • There is a default toString() method for each object ;). It might help if your question explains why you're not happy with that one! – ivy Jul 24 '11 at 15:41
  • 1
    @ivy, the default toString() method is the one in Object, which is essentially useless. OP has said he wants to see fields and values. – CPerkins Jul 24 '11 at 16:30
  • See also [Auto-generating toString Method](http://stackoverflow.com/questions/2653268/auto-generating-tostring-method) – Vadzim Dec 06 '13 at 18:33

8 Answers8

27

You are welcome to use ToStringBuilder from jakarta. It has 2 modes one requires adding all fields you need using API, other is reflection based:

@Override
public String toString() {
    return ToStringBuilder.reflectionToString(this);
}
AlexR
  • 114,158
  • 16
  • 130
  • 208
6

Use the JUtils plugin to generate the toString method at package level

Or using reflection you could try something like this:

public static void toString(Object object) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException{
        Class c = object.getClass();
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            if(method.getName().startsWith("get")){
                System.out.println(method.getName()+" -> " +method.invoke(object, null));
            }
        }
    }

The JUtil plugin would be best because that will allow you to change the toString of a object later on if you need provide more information about the object.

Varun Achar
  • 14,781
  • 7
  • 57
  • 74
3

If you hava a lot of objects then instead of adding toString in every class use code instrumentation (you can alter .class in jars or change them at loadtime using javaagent). For example AspectJ would help, but there are lots other alternatives. For example you can do such a thing (using AspectJ and ToStringBuilder from Apache Commons):

@Aspect
public class ToStringAspect {

     @Around("execution(String *.toString()) &&  target(t) && within(your.target.package.*)")
     public String toStringCall(Object t) {
        return ToStringBuilder.reflectionToString(t);
    }
}

ToStringBuilder is very flexible. e.g. if you don't need class name use style by setting StandardToStringStyle#setUseClassName(false):

 public String toStringCall(Object t) {
     StandardToStringStyle style = new StandardToStringStyle();
     style.setUseClassName(false);
     style.setUseIdentityHashCode(false);
    ToStringBuilder builder = new ReflectionToStringBuilder(t, style);
    return builder.toString();
}
zacheusz
  • 8,750
  • 3
  • 36
  • 60
2

There are few ways to implement toString method.

  1. Reflections (Apache library)

    @Override
    public String toString(){
        return org.apache.commons.lang3.builder.ReflectionToStringBuilder.toString(this);
    }
    
  2. JSON based implementation (GSON, Jackson libraries)

    // GSON library for JSON
    @Override
    public String toString(){
        return new com.google.gson.Gson().toJson(this);
    }
    
    // Jackson libabry for JSON/YAML
    @Override
    public String toString() {
        try {
            return new com.fasterxml.jackson.databind.ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
    
  3. ToStringBuilder (available with apache-commons library)

    @Override
    public String toString() {
        return new org.apache.commons.lang3.builder.ToStringBuilder(this).
            append("field1", field1).
            append("field2", field2).
            toString();
    }
    
  4. Hard-core toString() implementation

    @Override
    public String toString() {
        return new StringBuilder()
            .append("field1:"+field1)
            .append("field2:"+field2)
            .toString();
    }
    
Amit Kaneria
  • 5,466
  • 2
  • 35
  • 38
0

As was mentioned, 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 ReflectionAnalyzer {

   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
0

The toString method is inherited by every Class in Java, from java.lang.Object. I don't see how you can avoid not implementing (actually overriding) Object.toString. Also, methods are defined for each class, and not for each object.

You could use a inheritance hierarchy to solve this, by having your classes inherit from a common class that overrides the default toString implementation. This implementation may use reflection to reflect on the fields of the class (not the parent class, but actually the class for the current object), and generate the string.

You can also investigate the use of bytecode weaving if you do not want to override the method at all. Personally, I do not think that the effort invested in bytecode weaving is not worth it, especially when you could have overridden toString.

Vineet Reynolds
  • 76,006
  • 17
  • 150
  • 174
  • if i use reflection, why do i need inheritance? i can use a static method in some util, pass the object, get its class and use reflection. no? – AAaa Jul 24 '11 at 15:56
  • And the `toString` method will continue to be called wouldn't it? Especially if you write code like `String aStr = yourObj + "A String";` or `System.out.println(yourObj);` or countless other variants where `toString` will be invoked. If you see @AlexR's answer, he is overriding the default implementation offered by `java.lang.Object`. – Vineet Reynolds Jul 24 '11 at 16:01
  • Ah wonderful, an unexplained downvote. Care to inform the ignorant here. – Vineet Reynolds Jul 25 '11 at 05:50
0

Some sort of static String dataToString(Data data) method might work. I don't have any knowledge of reflection, so this my idea outside of that.

The method implementation would depend on how the data objects are setup. Is there inheritance going on? If there is, you could just pass in an object of type superclass as a parameter, and use the method globally for all data. You would need to do some conditional checking or other form of logic within the method, depending on how exactly your program is setup, to return the correct string.

Some more information about your program (how it's setup, what you want to print, what the data objects are, etc) would help me make this answer better :)

AlexFZ
  • 1,439
  • 1
  • 13
  • 22
  • How do i know which fields each child class has? – AAaa Jul 24 '11 at 15:54
  • You could do an "instanceof" check to get the exact child class. But like I said, I'd really need more details about your program to give you a better answer. – AlexFZ Jul 24 '11 at 15:55
  • Its a general problem of getting class's fields at runtime. do you mean using instanceof to know which class is it, and at design time manually get all fields of that class? because this is not what i'm looking for. I need something generic to be able to print any objects fields without any class-specific implemention. – AAaa Jul 24 '11 at 15:59
0

If you would like to implement toString() for every object, you could use AspectJ. Outside of something like this, you are going to be stuck implementing the method for every object.

Trevor
  • 398
  • 3
  • 6