概述

Project Lombok 是一个工具,它帮助我们在 Java 源代码中减少冗余任务。本教程将解释如何在 Lombok 中通过声明局部 valvar 变量来推断类型。

2. 声明 valvar 变量在 Lombok 中

Lombok 提供了智能能力来避免样板代码,例如,它会隐藏领域模型对象的 getter 和 setterBuilder 注解是另一个有趣的功能,它有助于正确实现 Builder 模式

接下来的章节我们将关注 Lombok 的一个功能,即不指定类型的定义局部变量。我们将使用 Lombok 的 valvar 类型来声明变量,从而减少源代码中的额外行。

  • val 在版本 0.10 中引入。使用 val 时,Lombok 将变量声明为 final,并在初始化后自动推断类型。因此,初始化表达式是必需的。
  • var 在版本 1.16.20 中引入。与 val 类似,它也会根据初始化表达式推断类型,但主要区别在于变量不会被声明为 final。因此,允许进一步的赋值,但应遵循在声明变量时指定的类型。

3. 在 Lombok 中实现 valvar 示例

3.1. 依赖项

为了实现示例,我们只需在 pom.xml 中添加 Lombok 依赖:

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

您可以在此处检查最新的可用版本:这里

3.2. val 变量声明

首先,我们导入 Lombok 的 val 类型:

import lombok.val;

然后,我们使用 val 声明不同的局部变量。例如,我们可以从一个简单的 String 开始:

public Class name() {
    val name = "name";
    System.out.println("Name: " + name);
    return name.getClass();
}

Lombok 自动生成如下基本的 Java 代码:

final java.lang.String name = "name";

接着,创建一个 Integer

public Class age() {
    val age = Integer.valueOf(30);
    System.out.println("Age: " + age);
    return age.getClass();
}

如您所见,Lombok 会生成正确的类型:

final java.lang.Integer age = Integer.valueOf(30);

我们还可以声明一个 List

public Class listOf() {
    val agenda = new ArrayList<String>();
    agenda.add("Day 1");
    System.out.println("Agenda: " + agenda);
    return agenda.getClass();
}

Lombok 不仅推断了 List 类型,还推断了其中元素的类型:

final java.util.ArrayList<java.lang.String> agenda = new ArrayList<String>();

现在,让我们创建一个 Map

public Class mapOf() {
    val books = new HashMap<Integer, String>();
    books.put(1, "Book 1");
    books.put(2, "Book 2");
    System.out.println("Books:");
    for (val entry : books.entrySet()) {
        System.out.printf("- %d. %s\n", entry.getKey(), entry.getValue());
    }
    return books.getClass();
}

同样,Lombok 会推断出适当的类型:

final java.util.HashMap<java.lang.Integer, java.lang.String> books = new HashMap<Integer, String>();
// ...
for (final java.util.Map.Entry<java.lang.Integer, java.lang.String> entry : books.entrySet()) {
   // ...
}

可以看到 Lombok 会声明为 final 的正确类型。因此,如果我们尝试修改变量名,由于 valfinal 性质,构建会失败:

name = "newName";

[12,9] cannot assign a value to final variable name

接下来,我们将运行一些测试以验证 Lombok 是否生成了正确的类型:

ValExample val = new ValExample();
assertThat(val.name()).isEqualTo(String.class);
assertThat(val.age()).isEqualTo(Integer.class);
assertThat(val.listOf()).isEqualTo(ArrayList.class);
assertThat(val.mapOf()).isEqualTo(HashMap.class);

最后,我们可以在控制台输出中看到具有特定类型的对象:

Name: name
Age: 30
Agenda: [Day 1]
Books:
- 1. Book 1
- 2. Book 2

3.3. var 变量声明

var 声明与 val 类似,但变量不是 final 的:

import lombok.var;

var name = "name";
name = "newName";

var age = Integer.valueOf(30);
age = 35;

var agenda = new ArrayList<String>();
agenda.add("Day 1");
agenda = new ArrayList<String>(Arrays.asList("Day 2"));

var books = new HashMap<Integer, String>();
books.put(1, "Book 1");
books.put(2, "Book 2");
books = new HashMap<Integer, String>();
books.put(3, "Book 3");
books.put(4, "Book 4");

让我们看看生成的基本 Java 代码:

var name = "name";

var age = Integer.valueOf(30);

var agenda = new ArrayList<String>();

var books = new HashMap<Integer, String>();

这是因为 Java 10 支持使用初始化表达式推断本地变量的类型,称为 类型推断。然而,在使用它时,我们需要考虑一些 限制

由于声明的变量不是 final,我们可以进行进一步的赋值。不过,对象必须符合初始化表达式中推断的适当类型

如果尝试分配不同的类型,编译时会出现错误:

books = new ArrayList<String>();

[37,17] incompatible types: java.util.ArrayList<java.lang.String> cannot be converted to java.util.HashMap<java.lang.Integer,java.lang.String>

让我们稍微调整测试,并检查新的赋值:

VarExample varExample = new VarExample();
assertThat(varExample.name()).isEqualTo("newName");
assertThat(varExample.age()).isEqualTo(35);
assertThat("Day 2").isIn(varExample.listOf());
assertThat(varExample.mapOf()).containsValue("Book 3");

最后,控制台输出与上一节有所不同:

Name: newName
Age: 35
Agenda: [Day 2]
Books:
- 3. Book 3
- 4. Book 4

4. 复合类型

有时,我们需要使用复合类型作为初始化表达式:

val compound = isArray ? new ArrayList<String>() : new HashSet<String>();

在上述片段中,赋值取决于布尔值,且最常见的是继承自的超类被推断。

Lombok 将类型分配为 AbstractCollection,如基本代码所示:

final java.util.AbstractCollection<java.lang.String> compound = isArray ? new ArrayList<String>() : new HashSet<String>();

在诸如 null 值这样的模糊情况下,推断为 Object 类。

5. 配置键

Lombok 允许在整个项目中在一个文件中配置功能。因此,可以在一个地方包含项目的全局指令和设置。

有时,作为项目开发标准的一部分,我们可能希望限制对 Lombok 的 varval 的使用。如果有人意外地使用它们,我们可能希望在编译时发出警告。

对于这种情况,我们可以通过在 lombok.config 文件中包含以下内容,将 varval 的任何使用标记为警告或错误

lombok.var.flagUsage = error
lombok.val.flagUsage = warning

我们将在整个项目中收到关于非法使用 var 的错误:

[12,13] Use of var is flagged according to lombok configuration.

同样,我们也会收到关于使用 val 的警告消息:

ValExample.java:18: warning: Use of val is flagged according to lombok configuration.
val age = Integer.valueOf(30);

6. 总结

在这篇文章中,我们展示了如何在 Lombok 中使用不指定类型的定义局部变量。此外,我们了解了声明 valvar 变量的细节。

我们还演示了如何处理复合类型的局部变量声明。

如往常一样,代码可在 GitHub 上找到。