1. Introduction

In this tutorial, we'll look at the Lombok @ToString annotation to generate the toString method automatically.

2. Maven Dependency

Let's first add the Lombok maven dependency:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

3. Use @ToString

The @ToString annotation generates the toString method automatically for us. By default, Lombok uses all non-static fields in the implementation of toString.

We'll work with the Developer class:

@ToString
public class Developer {

    private String name;
    private String language;
}

It has 2 fields, name and language. Also, we're annotating the class with @ToString.

When we compile our code, Lombok generates the toString method:

public class Developer {

    private String name;
    private String language;

    public String toString() {
        return "Developer(name=" + this.name + ", language=" + this.language + ")";
    }
}

Here,  the toString() method prints each field and its value separated by a comma. Additionally, Lombok accesses fields directly since there are no getters. However, if there were getters, Lombok would use them.

4. Exclude Fields for @ToString

Lombok provides several ways to exclude a field from the implementation of toString.

4.1. Exclude at Class Level

The @ToString annotation includes the exclude attribute. By using this attribute, we can list the field names that we want to exclude from the generation of toString:

@ToString(exclude = {"language", "salary"})
public class Developer {

    private String name;
    private String language;
    private int salary;
}

Here, the Developer class has three instance fields, name, language, and salary. Then we're excluding language and salary in the @ToString annotation.

Let's look at the generated code:

public class Developer {

    private String name;
    private String language;
    private int salary;

    public String toString() {
        return "Developer(name=" + this.name + ")";
    }
}

As expected, Lombok uses only the name field in the toString method.

4.2. Exclude at Field Level

Alternatively, we can exclude a field by annotating it with @ToString.Exclude:

@ToString
public class Developer {

    private String name;
    @ToString.Exclude
    private String language;
    @ToString.Exclude
    private int salary;
}

In this example, we're annotating the language and salary fields.

Similar to the previous example, toString should only include the name field:

public class Developer {

    private String name;
    private String language;
    private int salary;

    public String toString() {
        return "Developer(name=" + this.name + ")";
    }
}

5. Include Fields for @ToString

There are different ways of telling Lombok to include only specific fields in the implementation of toString.

5.1. Include at Field Level

Firstly, we can include specific fields by annotating them with @ToString.Include. This annotation is generally used with the onlyExplicitlyIncluded attribute of @ToString:

@ToString(onlyExplicitlyIncluded = true)
public class Developer {

    @ToString.Include
    private String name;
    private String language;
    @ToString.Include
    private int experienceInYears;
}

In the Developer class, since we're setting onlyExplicitlyIncluded to true, Lombok will only use the properties that are annotated with @ToString.Include.

public class Developer {

    private String name;
    private String language;
    private int experienceInYears;

    public String toString() {
        return "Developer(name=" + this.name + ", experienceInYears=" + this.experienceInYears + ")";
    }
}

As a result, Lombok doesn't use the language field in the toString implementation.

5.2. Include at Class Level

Another way to specify the included fields is by using the @ToString's of attribute. Consequently, Lombok won't use these fields when generating the toString method.

@ToString(of = {"name", "experienceInYears"})
public class Developer {

    private String name;
    private String language;
    private int experienceInYears;
}

When we compile our code, we get a toString method similar to the previous example:

public class Developer {

    private String name;
    private String language;
    private int experienceInYears;

    public String toString() {
        return "Developer(name=" + this.name + ", experienceInYears=" + this.experienceInYears + ")";
    }
}

5.3. Include at Method Level

Besides the instance fields, we can also annotate methods with @ToString.Include. This way, the return value of the method will be used in the toString method:

@ToString
public class Developer {

    private String name;
    private String language;
    private int experienceInYears;

    @ToString.Include
    public boolean isJunior() {
        return experienceInYears < 2;
    }
}

Here, we're defining the isJunior method which returns a boolean value. Since we're annotating isJunior with @ToString.Include, the generated toString method must call it:

public class Developer {

    private String name;
    private String language;
    private int experienceInYears;

    @ToString.Include
    public boolean isJunior() {
        return experienceInYears < 2;
    }

    public String toString() {
        return "Developer(name=" + this.name + ", language=" + this.language + ", experienceInYears=" + this.experienceInYears
          + ", isJunior=" + this.isJunior() + ")";
    }
}

6. Inheritance and Call Super Class

If our target class is extending another class, @ToString doesn't call the parent toString method.

To change this, we must set the callSuper attribute as true:

@ToString(callSuper = true)
public class Developer extends Person {

    private String name;
    private String language;
}

As a result, Lombok generates a toString method with a call to superclass:

public class Developer extends Person {

    private String name;
    private String language;

    public String toString() {
        return "Developer(super=" + super.toString() + ", name=" + this.name + ", language=" + this.language + ")";
    }
}

However, this configuration takes effect only for the current class. In order to enable calling superclass globally, we must use the lombok.toString.callSuper property - inside the lombok.config file:

# [call | skip | warn] (default: skip)
lombok.toString.callSuper = call

The default value is skip, so we're setting it to call. As a result, all @ToString generated methods will include a call to the parent toString method.

7. Include Field Names

Lombok includes the field names when constructing the return value of toString. However, we can disable this feature.

As the first option, we can disable it by setting includeFieldNames to false in the @ToString annotation:

@ToString(includeFieldNames = false)
public class Developer {

    private String name;
    private String language;
}

Let's look at the generated code:

public class Developer {

    private String name;
    private String language;

    public String toString() {
        return "Developer(" + this.name + ", " + this.language + ")";
    }
}

The return value includes only field values, not the names:

As the second option, we can configure it globally by using the lombok.toString.includeFieldNames property:

# [true | false] (default: true)
lombok.toString.includeFieldNames = false

Since the default value if true, we're setting lombok.toString.includeFieldNames as false. Consequently, all @ToString generated methods won't include the field names.

8. Configure @ToString Globally

Lombok provides several configuration properties for the @ToString annotation. For example, we've investigated lombok.toString.callSuper and lombok.toString.includeFieldNames in the previous sections.

So let's look at other properties.

8.1. lombok.toString.doNotUseGetters

Lombok uses getter methods - if available - instead of accessing the fields directly. While we can use the doNotUseGetters attribute of @ToString per class, to configure it globally we must use the lombok.toString.doNotUseGetters property:

# [true | false] (default: false)
lombok.toString.doNotUseGetters = true

Here, we're setting the value as true, while the default value is false.

8.2. lombok.toString.flagUsage

We can prevent the usage of @ToString by using the lombok.toString.flagUsage property:

# [warning | error] (default: not set)
lombok.toString.flagUsage = error

There is no default value for this configuration. In this example, we're setting the value as error. As a result, when Lombok detects usage of @ToString during the compilation, it fails the build and logs an error message.

9. Common Issues

Now, we'll look at the common issues related to the usage of @ToString.

9.1. Break Recursion

Bidirectional relationships between classes may result in java.lang.StackOverflowError, if both classes are using @ToString. To prevent recursive calls, we must exclude some fields in @ToString.

We'll start with the Developer and Manager classes:

@Getter
@Setter
@ToString
public class Developer {

    private String name;
    private String language;
    private Manager manager;
}

@Getter
@Setter
@ToString
public class Manager {

    private String name;
    private Developer subordinate;
}

Note that each class has a reference to the other.

Then, when we call toString on Developer with this current configuration, the application throws a StackOverflowError:

Exception in thread "main" java.lang.StackOverflowError
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:125)
at java.lang.AbstractStringBuilder.appendNull(AbstractStringBuilder.java:493)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:446)
at java.lang.StringBuilder.append(StringBuilder.java:136)
...

To fix the error, we must exclude the manager field from @ToString in Developer:

@Getter
@Setter
@ToString(exclude = "manager")
public class Developer {

    private String name;
    private String language;
    private Manager manager;
}

10. Summary

In this tutorial, we've examined how we can use the Lombok @ToString annotation to generate the toString method.

Finally, check out the source code for all examples in this article over on Github.