Java: Getting the properties of a class to construct a string representation

61,203

Solution 1

You could do:

@Override
public String toString() {
  StringBuilder sb = new StringBuilder();
  sb.append(getClass().getName());
  sb.append(": ");
  for (Field f : getClass().getDeclaredFields()) {
    sb.append(f.getName());
    sb.append("=");
    sb.append(f.get(this));
    sb.append(", ");
  }
  return sb.toString();
}

Don't use string concatenation to construct an end result from 15 data members, particularly if the toString() will be called a lot. The memory fragmentation and overhead could be really high. Use StringBuilder for constructing large dynamic strings.

I usually get my IDE (IntelliJ) to simply generate toString() methods for me rather than using reflection for this.

Another interesting approach is to use the @ToString annotation from Project Lombok:

import lombok.ToString;

@ToString(excludes="id")
public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;

  @ToString(callSuper=true, includeFieldNames=true)
  public static class Square extends Shape {
    private final int width, height;

    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

I find this much more preferable to, say, Jakarta Commons toString builders because this approach is far more configurable and it's also built at compile-time not run-time.

Solution 2

Check this API org.apache.commons.lang.builder.ToStringBuilder, it provides multiple ways to create toString usinf reflection or without reflection. Take a look at other subclasses as well.

Solution 3

There is such an api, and it is called Java Reflection

To accomplish what you are requesting, you can simply do something like:

 Class<?> cls = this.getClass();
 Field fieldlist[] = cls.getDeclaredFields();
 for (Field aFieldlist : fieldlist) {
   // build toString output with StringBuilder()
 }

Solution 4

This should be exactly what you are looking for

    public String toString() {
    StringBuilder sb = new StringBuilder();
    try {
        Class c = Class.forName(this.getClass().getName());
        Method m[] = c.getDeclaredMethods();
        Object oo;

        for (int i = 0; i < m.length; i++)
            if (m[i].getName().startsWith("get")) {
                oo = m[i].invoke(this, null);
                sb.append(m[i].getName().substring(3) + ":"
                        + String.valueOf(oo) + "\n");
            }
    } catch (Throwable e) {
        System.err.println(e);
    }
    return sb.toString();
}

Solution 5

Most IDEs provide a way to create a toString method in a given class.

Given an Item class with multiple fields:

class Item {
    int i;
    int j;
    int k;
    int l;
    int m;
    int n;
    int o;  
}

For example, in Eclipse, performing "Generate toString()" feature on the Item class above will create the following:

@Override
public String toString() {
    return "Item [i=" + i + ", j=" + j + ", k=" + k + ", l=" + l + ", m="
            + m + ", n=" + n + ", o=" + o + "]";
}

Using reflection would allow a programmatic way to observe one's own fields, but reflection itself is a fairly expensive (read: slow) process, so unless it is truly required, using a fixed toStringmethod written at runtime is probably going to be more desirable.

Share:
61,203

Related videos on Youtube

Legend
Author by

Legend

Just a simple guy :)

Updated on July 09, 2022

Comments

  • Legend
    Legend almost 2 years

    Let's say I have a class like this (and also further assume that all the private variables:

    public class Item {
        private String _id = null;
        private String _name = null;
        private String _description = null;
    
            ...
    }
    

    Now, if I want to build a toString() representation of this class, I would do something like this inside the Item class:

    @Override
    public String toString() {
        return (_id + " " + _name + " " + _description);
    }
    

    But what if I have say 15 private variables inside the class? Do I have to write the name of each and every variable like this?

    Ideally, I would like to get over with the task by iterating through the list of private variables of this class and construct the string representation:

    @Override
    public String toString() {
        ArrayList<String> members = getClass().getMembers(); //Some method like this
        String string = "";
        for(...)
            string += members[i] + " ";
    }
    

    Or perhaps a toJSON method, I would still need access to the names of these variables. Any suggestions?

  • Legend
    Legend over 14 years
    Actually the only reason I was thinking of Reflection is because I need to write a toString and a toJSON representation. I can write them manually but sometimes the classes represent items from a database so they could have a lot of fields. Would you still suggest a manual approach?
  • coobird
    coobird over 14 years
    It's really going to be up to the performance requirements of the toString and toJSON methods. If performance is not a concern, then using reflection probably isn't going to matter. If performance is a concern, it's probably going to be a good idea to profile the "manually" written code and code written using reflection, and see if the difference is going to matter.
  • Legend
    Legend over 14 years
    Understood. Thanks for the explanation. I think I will need a performance. I am writing a REST web service so I guess the answer is pretty clear from your explanation.
  • coobird
    coobird over 14 years
    As always, when it comes to performance, it would be prudent to profile some code to be sure -- don't just take other people's words ;)
  • Legend
    Legend over 14 years
    :) Thank You for that. I will go with the eclipse generation method for the toString representation and a reflection for toJSON and will profile it.
  • Legend
    Legend over 14 years
    Thank you for this. I would go with the first approach depending on the profiling results because the second approach uses one more library. I might have to write the toJSON for just three to four objects so I'll leave it to the profiling to decide.
  • Legend
    Legend over 14 years
    Thanks. This looks very interesting. Especially the method that uses no reflection.
  • Roel Spilker
    Roel Spilker about 14 years
    For Lombok, you only need a library during compilation. As long as you don't need @SneakyThrows, thare are no runtime-dependencies in Lombok