Generic toString() for JPA base entity class In a Java/JPA project I usually define a base class (on which I put the id). The goal is to implement an generic toString() on this base class (named BaseEntity) from which all other entities inherit. We played around with apache commons ReflectionToStringBuilder, but we ran into problems because of cyclic references and the lack of different handling depending on the type of the fields. 

My wish is that, if an Entity is printed/logged I get all field names and their values except for fields of type collection, in this case we just want the IDs of these objects to avoid recursion (PS: We can assume that collections on entities always contain other entities and they have an ID)

The desired output pattern is the following

classname[fieldname1=value1, fieldname2=value2,..., collectionname1=[id1,id2,..], collectionname2=[id1,id2,...],...]


a sample output for an entity "Project" which subclasses BaseEntity may look like this:

Project[nameLong=Testproject, nameShort=tpr, support=false, active=true, priority=0, blockers=[20], responsibilities=[13,14,15]]


We came up with the following implementation using reflection:

@Override
public String toString() {
    // init container for field names
    List&lt;String&gt; fieldToPrintout = new ArrayList&lt;String&gt;(0);

    // get all fields from this object (use declared fields to get private
    // and protected fields)
    Field[] fieldsOnTheObject = this.getClass().getDeclaredFields();

    // set the accessibility for field which are private
    AccessibleObject.setAccessible(fieldsOnTheObject, true);


    for (Field field : fieldsOnTheObject) {
        try {
            // ignore static fields
            if (!Modifier.isStatic(field.getModifiers())) {
                // get the object from the field
                Object fieldValue = field.get(this);
                // check if the field is a collection
                if (Collection.class.isAssignableFrom(field.getType())) { 
                    // check if the collection generic type is BaseEntity
                    if (field.getGenericType() instanceof ParameterizedType) {
                        Class&lt;?&gt; genericType = (Class&lt;?&gt;) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
                        if (BaseEntity.class.isAssignableFrom(genericType)) {
                            // iterate over all BaseEntities and get all id's
                            List&lt;String&gt; ids = ((Collection&lt;BaseEntity&gt;) fieldValue).stream().map(p -&gt; p.getId().toString()).collect(Collectors.toList());
                            fieldToPrintout.add(field.getName() + "=[" + String.join(",", ids) + "]");
                        }
                    }
                } else { // --&gt; no collection
                    // note: Field.get(Object) creates wrapper objects for primitive
                    fieldToPrintout.add(field.getName() + "=" + fieldValue);
                }
            }
        } catch (IllegalAccessException ex) {
            logger.catching(ex);
        }
    }
    return MessageFormat.format("{0}[{1}]", this.getClass().getSimpleName(), String.join(", ", fieldToPrintout));
}


I'm currently not really happy with the code, I'm sure it can be improved/simplified. Any suggestions?
So, there are a couple of things that can be done with this. First, use methods! Short, well-named methods can make it much easier to read the code. Second, use short-circuiting logic. continue is your friend. Most of your comments don't really contribute much, or can be obviated with good method names. You can also set the size of the ArrayList at creation time, because you know how many fields you're going to be dealing with. In my opinion, something like this looks a lot cleaner:

@Override
public String toString() {

    final Field[] fields = this.getClass().getFields();
    final List&lt;String&gt; fieldsToPrintOut = new ArrayList&lt;String&gt;(fields.length);

    AccessibleObject.setAccessible(fields, true);

    for (final Field field : fields) {
        if (Modifier.isStatic(field.getModifiers())) {
            continue;
        }

        final Object fieldValue;
        try {
            fieldValue = field.get(this);
        } catch (final IllegalAccessException ex) {
            logger.catching(ex);
            continue;
        }

        if (!isCollection(field)) {
            fieldsToPrintOut.add(field.getName() + "=" + fieldValue);
            continue;
        }

        if (!isParameterized(field) || !isBaseEntity(field)) {
            continue;
        }

        final List&lt;String&gt; ids =
                ((Collection&lt;BaseEntity&gt;) fieldValue).stream().map(p -&gt; p.getId().toString()).collect(Collectors.toList());
        fieldsToPrintOut.add(field.getName() + ids);

    }
    return MessageFormat.format(this.getClass().getSimpleName() + fieldsToPrintOut);
}

private static boolean isCollection(final Field field) {
    return Collection.class.isAssignableFrom(field.getType());
}

private static boolean isParameterized(final Field field) {
    return field.getGenericType() instanceof ParameterizedType;
}

private static boolean isBaseEntity(final Field field) {
    final Class&lt;?&gt; genericType =
            (Class&lt;?&gt;) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];

    return BaseEntity.class.isAssignableFrom(genericType);
}