Tired of writing toString() on all your classes? Annoyed when your co-workers forget to do so? Here’s a simple solution that uses reflection to implement toString().
Find two source files. ToString.java is a helper class for implementing toString(). It’s a revision of the Google Guava’s ToStringHelper.
public class ToString { private final List<ValueHolder> valueHolders = new LinkedList<ValueHolder>(); private boolean omitNullValues = false; protected final Object obj; public ToString(Object obj) { this.obj = obj; } public ToString add(String name, Object value) { addHolder(value).builder.append(name).append('=').append(value); return this; } public ToString add(String name, boolean value) { checkNameAndAppend(name).append(value); return this; } public ToString add(String name, char value) { checkNameAndAppend(name).append(value); return this; } public ToString add(String name, double value) { checkNameAndAppend(name).append(value); return this; } public ToString add(String name, float value) { checkNameAndAppend(name).append(value); return this; } public ToString add(String name, int value) { checkNameAndAppend(name).append(value); return this; } public ToString add(String name, long value) { checkNameAndAppend(name).append(value); return this; } private StringBuilder checkNameAndAppend(String name) { return addHolder().builder.append(name).append('='); } @Override public String toString() { // create a copy to keep it consistent in case value changes boolean omitNullValuesSnapshot = omitNullValues; boolean needsSeparator = false; StringBuilder builder = new StringBuilder(32).append( obj.getClass().getSimpleName()).append('{'); for (ValueHolder valueHolder : valueHolders) { if (!omitNullValuesSnapshot || !valueHolder.isNull) { if (needsSeparator) { builder.append(", "); } else { needsSeparator = true; } CharSequence sequence = valueHolder.builder; builder.append(sequence); } } return builder.append('}').toString(); } private ValueHolder addHolder() { ValueHolder valueHolder = new ValueHolder(); valueHolders.add(valueHolder); return valueHolder; } private ValueHolder addHolder(Object value) { ValueHolder valueHolder = addHolder(); valueHolder.isNull = (value == null); return valueHolder; } private static final class ValueHolder { final StringBuilder builder = new StringBuilder(); boolean isNull; } }
ReflectiveToString.java extends the former, and re-implements toString() to automatically add all (most) fields.
public class ReflectiveToString extends ToString { private static final Pattern[] IGNORE_FIELD_PATTERNS = new Pattern[] { Pattern.compile("^this\\$\\d+$"), Pattern.compile("^serialVersionUID$") }; public ReflectiveToString(Object obj) { super(obj); } @Override public String toString() { Set<Field> fields = getFields(); fields: for (Field f : fields) { String fn = f.getName(); for (Pattern p : IGNORE_FIELD_PATTERNS) { Matcher m = p.matcher(fn); if (m.matches()) { continue fields; } } f.setAccessible(true); try { add(fn, f.get(obj)); } catch (IllegalArgumentException e) { // silently skip, should never happen } catch (IllegalAccessException e) { // silently skip, should never happen } } return super.toString(); } private Set<Field> getFields() { Set<Field> fields = new HashSet<Field>(); getFields(obj.getClass(), fields); return fields; } private static void getFields(Class<? extends Object> cls, Set<Field> fields) { Class<? extends Object> superclass = cls.getSuperclass(); if (superclass != null) { getFields(superclass, fields); } fields.addAll(Arrays.asList(cls.getDeclaredFields())); } }
To use it, implement toString() on your classes like this,
@Override public String toString() { return new ReflectiveToString(this).toString(); }
You could of course extend Guava’s ToStringHelper directly. I didn’t want to pull in the entire library however.
Of course the string output can be tuned by modifying ToString.java.
As a side note, I was looking into using reflection to implement hashCode() and equals(). The reason why not is performance. Those methods are called A LOT, and a reflective version of equals was measured at around 100x slower than the non-reflective version. By the same token, the above toString() code should be used with caution.