Code styles
Code style determines how the generated method works and what classes it uses. There are several code styles available to chose from the combo box in generator's dialog.String concatenation
This style uses simple sum expressions so it's very efficient and relatively easy to read and modify. Here's an example outcome in the simplest case:return "FooClass [aFloat=" + aFloat + ", aString=" + aString + ", anInt=" + anInt
+ ", anObject=" + anObject + "]";
With "Skip null values" option turned on, the code becomes a little harder to read:return "FooClass [aFloat=" + aFloat + ", "
+ (aString != null ? "aString=" + aString + ", " : "")
+ "anInt=" + anInt + ", "
+ (anObject != null ? "anObject=" + anObject : "") + "]";StringBuilder/StringBuffer
This style uses StringBuilder if project is compatible with JDK1.5 or later and StringBuffer otherwise.StringBuilder builder = new StringBuilder();
builder.append("FooClass [aFloat=");
builder.append(aFloat);
builder.append(", aString=");
builder.append(aString);
builder.append(", anInt=");
builder.append(anInt);
builder.append(", anObject=");
builder.append(anObject);
return builder.toString();
The "Skip null values" option doesn't obfuscate the code as much as previously:StringBuilder builder = new StringBuilder();
builder.append("FooClass [aFloat=");
builder.append(aFloat);
builder.append(", ");
if (aString != null) {
builder.append("aString=");
builder.append(aString);
builder.append(", ");
}
builder.append("anInt=");
builder.append(anInt);
builder.append(", ");
if (anObject != null) {
builder.append("anObject=");
builder.append(anObject);
}
return builder.toString();String.format();
This style is very pleasant with relatively short list of elements, but with longer ones it becomes hard to see which fields are associated with which variables. Unfortunately, the "Skip null values" option is ignored by this style.return String.format("FooClass [aFloat=%s, aString=%s, anInt=%s, anObject=%s]",
aFloat, aString, anInt, anObject);
For JDK1.4 and earlier, the code is slightly different:
UPDATE: As I learned today, there's noString.format()
in JDK 1.4, soMessageFormat.format()
will be used instead:return MessageFormat.format("FooClass [aFloat={1}, aString={2}, anInt={3}, anObject={4}]",
new Object[] { Float.valueOf(aFloat), aString, Integer.valueOf(anInt), anObject });Apache Commons-Lang ToStringBuilder
When this style is chosen, format template is ignored because ToStringBuilder takes care the output string's format itself. Maybe it's a little less flexible, but the power of this solution is that you can easily change the style of all toStrings within the project without changing any actual object's toString method.ToStringBuilder builder = new ToStringBuilder(this);
builder.append("aFloat", aFloat);
builder.append("aString", aString);
builder.append("anInt", anInt);
builder.append("anObject", anObject);
return builder.toString();
Skipping nulls works this way:ToStringBuilder builder = new ToStringBuilder(this);
builder.append("aFloat", aFloat);
if (aString != null)
builder.append("aString", aString);
builder.append("anInt", anInt);
if (anObject != null)
builder.append("anObject", anObject);
return builder.toString();Spring Framework's ToStringCreator
This style behaves the same as Apache ToStringCreator except it uses different class to create output string.
Format templates
This is a simple mechanism that allows you to change format of generated method's output string: beginning, ending, separator, and so on. There are four tokens to use:${class.name} inserts the class name as a String ${member.name} inserts a member's name ${member.value} inserts a member's value ${otherMethods} this token must stand between the separator and the ending string
This is the template used for all examples in the previous part of this post:${class.name} [${member.name}=${member.value}, ${otherMembers}]
And of course, output string for this template looks like this:FooClass[aFloat=1.0, aString=hello, anInt=10, anObject=null]
- inserting super.toString() and hashCode() (now they can be printed the same way as other methods which is not convenient)
- ${class.getName} to use
this.getClass.getName()
instead of plain string - two different tokens for printing method names with or without parenthesis at the end
- I'm thinking about more type specific options, e. g. putting strings between quotation-marks, printing integers as hexadecimal, printing length of arrays and so on. But don't know how they should work...
Arrays handling
When "Ignore arrays' default toString()" option is switched on, generated toString() method lists items contained in arrays, for exampleintArray = [1, 2, 3, 5, 8, 13]
instead ofintArray = [I@9304b1
This is realized differently according to chosen JDK compatibility. For JDK1.5 and laterjava.util.Arrays.toString()
is used. For earlier versions, which do not have this method, a helperarrayToString()
method is generated:private String arrayToString(Object array, int length) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
for (int i = 0; i < length; i++) {
if (i > 0)
stringBuffer.append(", ");
if (array instanceof Object[])
stringBuffer.append(((Object[]) array)[i]);
if (array instanceof float[])
stringBuffer.append(((float[]) array)[i]);
if (array instanceof int[])
stringBuffer.append(((int[]) array)[i]);
}
stringBuffer.append("]");
return stringBuffer.toString();
}
It takes object as a parameter and then usesinstanceof
so that one method can work for all kinds of arrays. It checks only these array types that are actually passed to it in the main toString() method, so in case new types are added,arrayToString
is regenerated every time toString generator is run.Limiting number of items
This option changes behavior of generated toString in case of Collections, Maps and Arrays(if "Ignore default arrays' toString()" option is on). Again, generated code differs for each JDK version.
In JDK1.6 collection or map is turned into an array (collection.toArray()
ormap.entrySet().toArray()
), thenArrays.copyOf()
is used to make it shorter andArrays.toString()
is called to print it out. Of course, usually it's also necessary to check for nulls. All in all, the code becomes fairly complicated, with something like this in the worst case:builder.append(hashMap != null ? Arrays.toString(Arrays.copyOf(hashMap.entrySet().toArray(),
Math.min(maxItem, hashMap.size()))) : null);
At least there's no need to generate additional methods.
SinceArrays.copyOf
was introduced in JDK1.6, in earlier versions helper toString() methods must be used to limit number of elements. In case of arrays the method is similar to the one showed in the previous part, only with one more statement at the beginning:length = Math.min(lenght, maxItem);
. For collections and maps there's another method:private String toString(Collection collection) {
StringBuilder stringBuilder = new StringBuilder();
final int maxItem = 10;
stringBuilder.append("[");
int i = 0;
for (Iterator iterator = collection.iterator();
iterator.hasNext() && i < maxItem; i++) {
if (i > 0)
stringBuilder.append(", ");
stringBuilder.append(iterator.next());
}
stringBuilder.append("]");
return stringBuilder.toString();
}
This method is not overwritten every time the generator runs so that it can be changed by user.
Another solution would be to convert a collection into an array and then usearrayToString
, but this way is more efficient and looks better if there are no arrays.Plans for the future
In addition to things I mentioned earlier, I'm going to create an extension point for new code styles and add toString generation to code assist. This time I won't be able to copy solutions from hashCode/equals generator though, so the work may not go as smooth as earlier. Still, I don't loose my optimism :)
I plan to define more tokens: