Lombok’s @ToString Annotation – Lombok’的@ToString注释

最后修改: 2022年 3月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

As we know, the toString() method is used to get the string representation of a Java object.

我们知道,toString()方法是用来获取一个Java对象的字符串表示。

Project Lombok can help us generate consistent string representations without the boilerplate and cluttering the source code. It can also improve maintainability, especially where classes might contain a large number of fields.

Project Lombok可以帮助我们生成一致的字符串表示法,而不需要模板和杂乱的源代码。它还可以提高可维护性,特别是在类可能包含大量字段的地方。

In this tutorial, we’ll see how to auto-generate this method and the various configuration options available to further fine-tune the resulting output.

在本教程中,我们将看到如何自动生成这种方法,以及可用来进一步微调所产生的输出的各种配置选项。

2. Setup

2.设置

Let’s start by including the Project Lombok dependency in our sample project:

让我们首先在我们的示例项目中包括Project Lombok依赖项。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

In our examples, we’ll use a simple Account POJO class with a few fields to demonstrate the functionality and various configuration options.

在我们的例子中,我们将使用一个简单的Account POJO类,用几个字段来演示功能和各种配置选项。

3. Basic Usage

3.基本使用方法

We can annotate any class with the Lombok @ToString annotation. This modifies the generated bytecode and creates an implementation of the toString() method.

我们可以用Lombok @ToString注解来注释任何类。这将修改生成的字节码并创建一个toString()方法的实现。

Let’s apply this annotation to our simple Account POJO:

让我们把这个注解应用于我们简单的Account POJO。

@ToString
public class Account {

    private String id;

    private String name;

    // standard getters and setters
}

By default, the @ToString annotation prints the class name, along with each non-static field name and its value obtained by calling the getter (if declared). The fields also appear according to the declaration order in the source class. A comma separates the different field value pairs.

默认情况下,@ToString注解会打印类的名称,以及每个非静态字段的名称和它通过调用getter(如果已声明)获得的值这些字段也根据源类中的声明顺序出现。逗号分隔了不同的字段值对。

Now, a call to the toString() method on an instance of this class generates this output:

现在,在这个类的一个实例上调用toString()方法会产生这样的输出。

Account(id=12345, name=An account)

In most cases, this is sufficient to generate a standard and useful string representation of Java objects.

在大多数情况下,这足以生成一个标准和有用的Java对象的字符串表示。

4. Configuration Options

4.配置选项

There are several configuration options available that allow us to modify and tweak the generated toString() method. These can be helpful in certain use cases. Let’s take a look at these in a little more detail.

有几个配置选项,允许我们修改和调整生成的toString()方法。这些选项在某些使用情况下可能会有帮助。让我们更详细地看一下这些选项。

4.1. Superclass toString()

4.1.超类toString()

By default, the output does not contain data from the superclass implementation of the toString() method. However, we can modify this by setting the callSuper attribute value to true:

默认情况下,输出不包含来自toString()方法的超类实现的数据。然而,我们可以通过将callSuper属性值设置为true来修改:

@ToString(callSuper = true)
public class SavingAccount extends Account {
    
    private String savingAccountId;

    // standard getters and setters
}

This produces the following output with the superclass information followed by the subclass fields and values:

这产生了以下输出,其中有超类的信息,后面是子类的字段和值。

SavingAccount(super=Account(id=12345, name=An account), savingAccountId=6789)

Importantly, this is only really beneficial when we extend a class other than java.lang.Object. The Object implementation of toString() doesn’t provide much useful information. In other words, including this data only adds redundant information as well as increases the output verbosity.

重要的是,只有当我们扩展java.lang.Object以外的类时,这才真正有益。Object的实现toString()并没有提供多少有用的信息。换句话说,包括这些数据只会增加多余的信息,以及增加输出的冗长性。

4.2. Omitting Field Names

4.2.省略字段名

As we saw earlier, the default output contains field names followed by the values. However, we can omit the field names from the output by setting the includeFieldNames attribute to false in the @ToString annotation:

正如我们前面看到的,默认的输出包含字段名,后面是值。然而,我们可以 通过在@ToString注解中把includeFieldNames属性设置为false,从输出中取消字段名。

@ToString(includeFieldNames = false)
public class Account {

    private String id;

    private String name;

    // standard getters and setters
}

As a result, the output now shows a comma-separated list of all the field values without the field names:

结果,现在输出显示的是一个逗号分隔的所有字段值的列表,而没有字段名。

Account(12345, An account)

4.3. Using Fields Instead of Getters

4.3.使用字段而不是获取器

As we’ve already seen, the getter methods provide the field values for printing. Additionally, if the class does not contain a getter method for a particular field, then Lombok directly accesses the field and obtains its value.

正如我们已经看到的,getter方法提供了用于打印的字段值。此外,如果该类不包含某个特定字段的getter方法,那么Lombok会直接访问该字段并获取其值。

However, we can configure Lombok to always use the direct field values rather than the getters by setting doNotUseGetters attribute to true:

然而,我们可以通过设置doNotUseGetters属性为true,将Lombok配置为总是使用直接字段值而不是getters。

@ToString(doNotUseGetters = true)
public class Account {

    private String id;

    private String name;

    // ignored getter
    public String getId() {
        return "this is the id:" + id;
    }

    // standard getters and setters
}

Without this attribute, we’d get the output obtained by calling the getters:

如果没有这个属性,我们会得到通过调用getters得到的输出。

Account(id=this is the id:12345, name=An account)

Instead, with the doNotUseGetters attribute, the output actually shows the value of the id field, without invoking the getter:

相反,使用doNotUseGetters属性,输出实际上显示了id字段的值,而没有调用getter

Account(id=12345, name=An account)

4.4. Field Inclusion and Exclusion

4.4.实地考察与排除

Let’s say that we want to exclude certain fields from the string representation, e.g., passwords, other sensitive information, or large JSON structures. We can omit such fields simply by annotating them with the @ToString.Exclude annotation.

假设我们想从字符串表示中排除某些字段,例如,密码、其他敏感信息或大型JSON结构。我们可以简单地用@ToString.Exclude注解省略这些字段

Let’s exclude the name field from our representation:

让我们把name字段从我们的表述中排除。

@ToString
public class Account {

    private String id;

    @ToString.Exclude
    private String name;

    // standard getters and setters
}

Alternatively, we can specify only the fields that are required in the output. Let’s accomplish this by using @ToString(onlyExplicitlyIncluded = true) at the class level and then annotating each required field with @ToString.Include:

另外,我们可以只指定输出中需要的字段。让我们通过在类级别使用@ToString(onlyExplicitlyIncluded = true),然后用@ToString.Include注释每个需要的字段来实现。

@ToString(onlyExplicitlyIncluded = true)
public class Account {

    @ToString.Include
    private String id;

    private String name;

    // standard getters and setters
}

Both approaches above produce the following output with only the id field:

上述两种方法都会产生以下只有id字段的输出。

Account(id=12345)

In addition, Lombok output automatically excludes any variables starting with the $ symbol. However, we can override this behavior and include them by adding the @ToString.Include annotation at the field level.

此外,Lombok输出自动排除任何以$符号开始的变量。然而,我们可以通过在字段级添加@ToString.Include注解来覆盖这一行为并包括它们。

4.5. Ordering Output

4.5.订购输出

By default, the output contains fields according to the declaration order in the class. However, we can adjust the ordering simply by adding the rank attribute to the @ToString.Include annotation.

默认情况下,输出包含的字段是按照类中的声明顺序。然而,我们可以通过在@ToString.Include注解中添加rank属性来调整顺序

Let’s modify our Account class so that the id field renders before any other fields regardless of the declaration position in the class definition. We can achieve this by adding the @ToString.Include(rank = 1) annotation to the id field:

让我们修改我们的Account类,以便id字段在任何其他字段之前渲染,无论在类定义中的声明位置如何。我们可以通过向id字段添加@ToString.Include(rank = 1)注解来实现。

@ToString
public class Account {

    private String name;

    @ToString.Include(rank = 1)
    private String id;

    // standard getters and setters
}

Now, the id field renders first in the output despite its declaration after the name field:

现在,尽管id字段的声明在name字段之后,但它在输出中显示在第一位。

Account(id=12345, name=An account)

The output contains members of a higher rank first, followed by lower ranks. The default rank value for members without the rank attribute is 0. Members with the same rank are printed according to their declaration order.

输出中首先包含等级较高的成员,其次是等级较低的成员。没有等级属性的成员的默认等级值是0。 具有相同等级的成员按照其声明顺序打印。

4.6. Method Output

4.6.方法输出

In addition to fields, it’s also possible to include the output of an instance method that takes no arguments. We can do this by marking the no-arg instance method with @ToString.Include:

除了字段之外,我们也可以包括一个不需要参数的实例方法的输出。我们可以通过用@ToString.Include来标记无参数的实例方法。

@ToString
public class Account {

    private String id;

    private String name;

    @ToString.Include
    String description() {
        return "Account description";
    }

    // standard getters and setters
}

This adds the description as the key and its output as the value to the Account representation:

这将description作为键添加到Account表示中,并将其输出作为值。

Account(id=12345, name=An account, description=Account description)

If the specified method name matches a field name, then the method takes precedence over the field. In other words, the output contains the result of the method invocation instead of the matching field value.

如果指定的方法名称与字段名称相匹配,那么该方法就会优先于字段。换句话说,输出包含方法调用的结果,而不是匹配字段的值。

4.7. Modifying Field Names

4.7.修改字段名

We can change any field name by specifying a different value in the name attribute of the @ToString.Include annotation:

我们可以通过在@ToString.Include注解的name属性中指定一个不同的值来改变任何字段名。

@ToString
public class Account {

    @ToString.Include(name = "identification")
    private String id;

    private String name;

    // standard getters and setters
}

Now, the output contains the alternative field name from the annotation attribute instead of the actual field name:

现在,输出包含来自注释属性的替代字段名,而不是实际的字段名。

Account(identification=12345, name=An account)

5. Printing Arrays

5.打印阵列

Arrays are printed using the Arrays.deepToString() method. This converts the array elements to their corresponding string representations. However, it’s possible that the array contains either a direct reference or an indirect circular reference.

使用Arrays.deepToString()方法打印数组。这可以将数组元素转换为其相应的字符串表示。然而,数组有可能包含直接引用或间接循环引用。

In order to avoid infinite recursion and its associated runtime errors, this method renders any circular references to the array from within itself as “[[…]]”.

为了避免无限递归及其相关的运行时错误,该方法将任何从自身内部对数组的循环引用渲染为“[[…]]”。

Let’s see this by adding an Object array field to our Account class:

让我们通过向我们的Account类添加一个Object数组字段来看看。

@ToString
public class Account {

    private String id;

    private Object[] relatedAccounts;

    // standard getters and setters
}

The relatedAccounts array is now included in the output:

relatedAccounts数组现在包括在输出中。

Account(id=12345, relatedAccounts=[54321, [...]])

Importantly, the circular reference is detected by the deepToString() method and rendered appropriately by Lombok, without causing any StackOverflowError.

重要的是,循环引用被deepToString()方法检测到,并且被Lombok适当地呈现,而没有引起任何StackOverflowError

6. Points to Remember

6.要记住的要点

There are several details worth mentioning that are important to avoid unexpected results.

有几个细节值得一提,对避免意外的结果很重要。

In the presence of any method named toString() in the class (regardless of the return type), Lombok does not generate its toString() method.

在类中存在任何名为toString()的方法(无论其返回类型如何),Lombok不会生成其toString()方法

Different versions of Lombok may change the output format from the generated method. In any case, we should avoid code that relies on parsing the toString() method output. So this shouldn’t really be a problem.

不同版本的Lombok可能会改变生成方法的输出格式。在任何情况下,我们应该避免依赖解析toString()方法输出的代码。所以,这其实不应该是一个问题。

Lastly, we can also add this annotation on enums. This produces a representation where the enum value follows the enum class name, e.g., AccounType.SAVING.

最后,我们也可以在enums上添加这个注释。这将产生一种表示方法,其中enum值跟随enum类名,例如,AccounType.SAVING

7. Conclusion

7.结语

In this article, we’ve seen how to use Lombok annotations to generate a String representation of Java objects with minimum effort and boilerplate.

在这篇文章中,我们已经看到了如何使用Lombok注解,以最小的努力和模板生成Java对象的String表示。

Initially, we looked at the basic usage, which is usually sufficient for most cases. We then covered a wide range of options available to tweak and tune the generated output.

最初,我们看了基本用法,这通常对大多数情况来说是足够的。然后,我们涵盖了一系列可用于调整和调节生成的输出的选项。

As always, the full source code is available over on GitHub.

一如既往,完整的源代码可在GitHub上获得