1. Overview

In this tutorial, we're going to look at the different ways to generate the toString method.

2. Using StringJoiner

Firstly, we'll use StringJoiner to generate expressive toString methods. Moreover, StringJoiner is part of the JDK so it doesn't require any additional dependency.

public class PersonUsingStringJoiner {

    private String firstName;
    private String lastName;

    // Getters and setters...

    @Override
    public String toString() {
        return new StringJoiner(", ", getClass().getSimpleName() + "[", "]")
          .add("firstName=" + firstName)
          .add("lastName=" + lastName)
          .toString();
    }
}

Here, we're declaring a new StringJoiner instance and designating the comma as the separator between fields. Then we're adding each field in the form of key/value pairs.

When we call toString:

@Test
public void testToString() {
    final PersonUsingStringJoiner person = new PersonUsingStringJoiner();
    person.setFirstName("john");
    person.setLastName("doe");

    final String toString = person.toString();

    System.out.println(toString);
}

It returns:

PersonUsingStringJoiner[firstName=john, lastName=doe]

3. Using Apache Commons

Next, we'll look at Apache Commons Lang which provides a host of helper methods.

We'll first add the commons-lang3 Maven dependency:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
</dependency>

Then we'll use the ToStringBuilder class to generate toString:

@Override
public String toString() {
    return new ToStringBuilder(this)
      .append("firstName", firstName)
      .append("lastName", lastName)
      .build();
}

Here, after creating an instance of ToStringBuilder, we're adding the instance fields that we want in the output.

When we call our toString method:

@Test
public void testToString_WhenUsingCommons() {
    final PersonUsingCommons person = new PersonUsingCommons();
    person.setFirstName("john");
    person.setLastName("doe");

    final String toString = person.toString();
    
    System.out.println(toString);
}

It returns:

com.javabyexamples.java.core.tostring.PersonUsingCommons@1b701da1[firstName=john,lastName=doe]

Here we have the fully qualified class name with the object's hash code. Then the fields are listed in the square brackets. This is the default output style, namely ToStringStyle.DEFAULT_STYLE.

Next, we'll look at the other available styles.

When we select ToStringStyle.SHORT_PREFIX_STYLE:

@Test
public void testToString_WhenUsingCommons_SHORT_PREFIX_STYLE() {
    final PersonUsingCommons person = getPerson();
    final String toString = new ToStringBuilder(person, ToStringStyle.SHORT_PREFIX_STYLE)
      .append("firstName", person.getFirstName())
      .append("lastName", person.getLastName())
      .build();
    
    System.out.println(toString);
}

It outputs the simple class name and the selected fields:

PersonUsingCommons[firstName=john,lastName=doe]

ToStringStyle.NO_CLASS_NAME_STYLE outputs only the selected fields - skipping the class name:

[firstName=john,lastName=doe]

On the other hand, ToStringStyle.NO_FIELD_NAMES_STYLE skips the field names:

com.javabyexamples.java.core.tostring.PersonUsingCommons@1b701da1[john,doe]

ToStringStyle.JSON_STYLE outputs the selected fields in JSON format:

{"firstName":"john","lastName":"doe"}

And lastly, ToStringStyle.SIMPLE_STYLE prints only the field values:

john,doe

So far, we have defined which fields we want in the output. We also explicitly defined the field names instead of using the variable names. Although this gives more control over the content, it is handy to have a more generic solution that returns all fields in our object. ReflectionToStringBuilder finds all instance fields including the ones in the superclass:

@Test
public void testToString_WhenUsingCommonsReflection() {
    final PersonUsingCommons person = getPerson();
    final String toString = ReflectionToStringBuilder.toString(person);
    
    System.out.println(toString);
}

It formats the output using ToStringStyle.DEFAULT_STYLE:

com.javabyexamples.java.core.tostring.PersonUsingCommons@15615099[firstName=john,lastName=doe]

4. Using Guava

Now, we'll look at the Guava library and use it to generate the toString method. We'll start with adding the guava Maven dependency:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.0-jre</version>
</dependency>

Now that we have the library, we'll use the MoreObjects helper class:

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
      .add("firstName", firstName)
      .add("lastName", lastName)
      .toString();
}

Here, we're calling MoreObjects.toStringHelper to start defining our toString method. Then, similar to ToStringBuilder, we're adding the instance fields.

When we call the toString method:

@Test
public void testToString() {
    final PersonUsingGuava person = new PersonUsingGuava();
    person.setFirstName("john");
    person.setLastName("doe");

    final String toString = person.toString();

    System.out.println(toString);
}

It outputs:

PersonUsingGuava{firstName=john, lastName=doe}

We can also omit the null values from the output:

@Test
public void testToString_WhenOmittingNullValues() {
    final PersonUsingGuava person = new PersonUsingGuava();
    person.setFirstName("john");
    person.setLastName(null);

    final String toString = MoreObjects.toStringHelper(person)
      .add("firstName", person.getFirstName())
      .add("lastName", person.getLastName())
      .omitNullValues()
      .toString();

    System.out.println(toString);
}

Here, we're calling omitNullValues after specifying the fields. As a result, the output doesn't include the null fields i.e. lastName:

PersonUsingGuava{firstName=john}

5. Using Lombok

Lastly, we'll use the Lombok @ToString annotation.

@ToString
public class PersonUsingLombok {

    private String firstName;
    private String lastName;

    // Getters and setters...
}

Here, we're annotating the class with @ToString. In return, Lombok generates a toString method using the instance fields of the class.

It outputs the simple class name and fields:

PersonUsingLombok(firstName=john, lastName=doe)

6. Summary

In this tutorial, we've investigated the different ways to generate the toString method.

We first looked at the StringJoiner approach. It is a lightweight solution since it doesn't require any additional library.

Then we covered some library-based solutions, which provide some handy features like reflection-based toString generation.

Lastly, check out the source code for all examples over on Github.