1. 概述

在日常开发中,实体类(POJO)几乎都会配备 getset 方法。虽然这是标准做法,但有时我们希望代码更简洁、更具表达力。

本文将深入介绍 Lombok 的 @Accessors 注解,它支持 流式调用(fluent)链式调用(chained)前缀剔除(prefix) 以及 方法设为 final(makeFinal) 等高级特性,让你的代码更优雅。

⚠️ 使用前请确保你的 IDE 已安装 Lombok 插件,否则编译会报错。


2. 标准访问器

在进入 @Accessors 之前,先回顾下 Lombok 默认的 @Getter@Setter 行为。

定义一个普通实体类:

@Getter
@Setter
public class StandardAccount {
    private String name;
    private BigDecimal balance;
}

测试其 getter/setter 是否正常工作:

@Test
public void whenStandardAccount_thenHaveStandardAccessors() {
    StandardAccount account = new StandardAccount();
    account.setName("Standard Accessors");
    account.setBalance(BigDecimal.TEN);

    assertEquals("Standard Accessors", account.getName());
    assertEquals(BigDecimal.TEN, account.balance());
}

✅ 这是“教科书式”的写法,但略显啰嗦。接下来我们看看如何用 @Accessors 改造它。


3. 流式访问器(Fluent Accessors)

使用 @Accessors(fluent = true) 可以生成无 get/set 前缀的方法。

⚠️ 注意:chain = true 是默认行为,这里我们先显式关闭以聚焦 fluent 特性。

@Accessors(fluent = true, chain = false)
@Getter
@Setter
public class FluentAccount {
    private String name;
    private BigDecimal balance;
}

测试代码变得更简洁:

@Test
public void whenFluentAccount_thenHaveFluentAccessors() {
    FluentAccount account = new FluentAccount();
    account.name("Fluent Account");        // 替代 setName()
    account.balance(BigDecimal.TEN);       // 替代 setBalance()

    assertEquals("Fluent Account", account.name());     // 替代 getName()
    assertEquals(BigDecimal.TEN, account.balance());    // 替代 getBalance()
}

✅ 方法名直接使用字段名,读起来像“配置项”,非常自然。


4. 链式访问器(Chained Accessors)

@Accessors(chain = true) 让 setter 返回 this,从而支持链式调用。

我们结合 fluent 一起使用:

@Accessors(fluent = true, chain = true)
@Getter 
@Setter 
public class ChainedFluentAccount { 
    private String name; 
    private BigDecimal balance;
}

✅ 实际上只写 @Accessors(fluent = true) 就够了,因为 chain 默认为 true

测试代码可以直接“串”起来:

@Test
public void whenChainedFluentAccount_thenHaveChainedFluentAccessors() {
    ChainedFluentAccount account = new ChainedFluentAccount()
      .name("Fluent Account")
      .balance(BigDecimal.TEN);

    assertEquals("Fluent Account", account.name()); 
    assertEquals(BigDecimal.TEN, account.balance());
}

✅ 这种写法极大减少了样板代码,类似 Builder 模式的效果,简单粗暴。

💡 提示:Lombok 的 @Builder 就是基于这种链式 fluent 风格实现的。


5. 前缀剔除(Prefix Accessors)

有些老项目字段名带前缀(匈牙利命名法),比如 sNamebdBalance。直接生成 getter 会变成 getSName(),非常别扭。

@Accessors(prefix = {...}) 可以指定要忽略的字段前缀:

@Accessors(prefix = {"s", "bd"})
@Getter
@Setter
public class PrefixedAccount {
    private String sName;
    private BigDecimal bdBalance;
}

测试验证前缀已被剔除:

@Test
public void whenPrefixedAccount_thenRemovePrefixFromAccessors() {
    PrefixedAccount account = new PrefixedAccount();
    account.setName("Prefixed Fields");
    account.setBalance(BigDecimal.TEN);

    assertEquals("Prefixed Fields", account.getName()); 
    assertEquals(BigDecimal.TEN, account.getBalance());
}

✅ 生成的方法是 getName() 而不是 getSName(),清爽多了。

前缀匹配规则

Lombok 的前缀匹配有两条关键规则,避免误伤:

  1. 前缀后必须紧跟大写字母
    例如字段 states 开头,但 t 是小写,所以不会被当作 s 前缀处理,不会生成 getTate() 这种离谱方法。

  2. 前缀本身以非字母结尾时也匹配
    比如字段 s_notes,前缀设为 "s_"

    @Accessors(prefix = "s_")
    private String s_notes;
    

    即使 _n 中的 n 是小写,但由于前缀 s__(非字母)结尾,仍会正确生成 setNotes()getNotes()


6. Final 访问器(Final Accessors)

从 Lombok v1.18.24 开始,支持 makeFinal = true,生成的 getter/setter 会被标记为 final,防止子类重写。

适用于那些不允许被覆盖的访问器:

@Accessors(makeFinal = true)
@Getter
@Setter
public class FinalAccount {
    private String name;
    private BigDecimal balance;
}

测试验证功能正常且方法为 final:

@Test
public void whenFinalAccount_thenHaveFinalAccessors() {
    FinalAccount account = new FinalAccount();
    account.setName("Final Account");
    account.setBalance(BigDecimal.TEN);

    assertEquals("Final Account", account.getName());
    assertEquals(BigDecimal.TEN, account.getBalance());

    // 使用反射验证所有 getter/setter 是否为 final
    boolean getterSettersAreFinal = Arrays.stream(FinalAccount.class.getMethods())
      .filter(method -> method.getName().matches("^(get|set)(Name|Balance)$"))
      .allMatch(method -> Modifier.isFinal(method.getModifiers()));
    
    assertTrue(getterSettersAreFinal);
}

✅ 安全性提升,尤其在继承体系中防止意外覆盖。

组合使用示例

makeFinal 可与其他选项叠加:

@Accessors(makeFinal = true, fluent = true, chain = true)
@Getter
@Setter
public class FinalChainedFluentAccount {
    private String name;
    private BigDecimal balance;
}

测试链式调用并验证方法为 final:

@Test
public void whenFinalChainedFluentAccount_thenHaveFinalAccessors() {
    FinalChainedFluentAccount account = new FinalChainedFluentAccount()
      .name("Final Chained Fluent Account")
      .balance(BigDecimal.TEN);

    assertEquals("Final Chained Fluent Account", account.name());
    assertEquals(BigDecimal.TEN, account.balance());

    // 验证 fluent 方法 name() 和 balance() 是否为 final
    boolean getterSettersAreFinal = Arrays.stream(FinalChainedFluentAccount.class.getMethods())
      .filter(method -> method.getName().matches("^(name|balance)$"))
      .allMatch(method -> Modifier.isFinal(method.getModifiers()));
    
    assertTrue(getterSettersAreFinal);
}

✅ 一行代码搞定对象初始化,同时保证方法不可重写,踩坑少。


7. 全局配置(Configuration Properties)

如果项目中大量使用某种风格(比如全用 fluent + chain),可以通过 lombok.config 文件统一配置:

lombok.accessors.chain = true
lombok.accessors.fluent = true

该配置对所在目录及其子目录生效。推荐放在项目根目录或 src/main/java 下。

✅ 从此无需在每个类上重复加注解,团队风格统一,省心省力。

更多配置项参考:Lombok 官方配置指南


8. 总结

@Accessors 是 Lombok 中一个低调但极其实用的注解,通过几个布尔选项就能显著提升代码可读性和简洁度。

关键特性回顾:

特性 作用 推荐场景
fluent = true 去掉 get/set 前缀 配置类、DSL 风格 API
chain = true setter 返回 this 链式初始化
prefix = {...} 忽略字段前缀 老系统兼容、匈牙利命名
makeFinal = true 方法标记为 final 防止子类重写,增强封装

✅ 建议在新项目中大胆使用 fluent + chain 组合,写出来的代码干净利落。

源码地址:https://github.com/yourname/tutorials/tree/master/lombok-modules/lombok-2


原始标题:Using Lombok's @Accessors Annotation