Search code examples
javareflectionclasstostringclass-variables

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


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?


Solution

  • 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.