Skip to content

使用Lombok神级插件简化代码

更新: 12/31/2025, 6:43:00 AM   字数: 0 字   时长: 0 分钟

GitHub | Lombok最新版本 | Lombok功能 | 安装教程

简介

​  Lombok插件是一种 Java 实用工具,通过注解的形式,可以帮助开发人员精简代码,尤其是对于简单的 Java 对象(POJO)。推荐使用的Lombok注解如下:

java
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper=true)
@EqualsAndHashCode(callSuper=true)
public class User {
    
}

插件安装

​  首先,需要在IntelliJ IDEA安装Lombok插件,安装教程参考这里。然后,在pom中引入lombok依赖。

xml
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.36</version>
</dependency>

组合注解

@Getter和@Setter

(1)在类上注释

java
@Getter
@Setter(AccessLevel.PROTECTED)
public class User{
    private String name;
}

等价于 Java 源码:

java
public class User {
    private String name;
    
    public String getName() {
        return this.name;
    }
    
    protected void setName(final String name) {
        this.name = name;
    }
}

(2)在属性上注释

java
public class User{
    @Getter @Setter(AccessLevel.PROTECTED) 
    private String name;
}

等价于 Java 源码:

java
public class User {
    private String name;

    public String getName() {
        return this.name;
    }

    protected void setName(String name) {
        this.name = name;
    }
}

@ToString

(1)重写toString方法

java
// callSuper: 是是否包含父类的属性,默认为false
@ToString(callSuper=true)
public class User extends Person{
    private String name;
    private String age;
}

等价于 Java 源码:

java
public class User extends Person{
    private String name;
    private String age;
    
    @Override
    public String toString() {
    	return "Demo02.User(super=" + super.toString() //// [!code ++]
            + ", name=" + this.name + ")";
    }
}

(2)排除指定字段

java
// 可以排除指定字段不参与toString()的判断
// 示例一:排除指定字段
@ToString(exclude="age")
public class User extends Person{
    private String name;
    private String age;
}

// 示例二:排除多个字段
@ToString(exclude={"address","age"})
public class User extends Person{
    private String name;
    private String age;
}

// 示例三:排除指定字段
public class User extends Person{
    @ToString.Exclude private String name;
    private String age;
}

@EqualsAndHashCode

(1)重新equals和hashCode方法

java
// callSuper: 判断是是否包含父类的属性,默认为false
@EqualsAndHashCode(callSuper=true)
public class User extends Person{
    private String name;
    private String age;
}
等价于 Java 源码
java
public class User extends Person {
    private String name;
    private String age;
    private String level;

    @Override
    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof User)) {
            return false;
        } else {
            User other = (User)o;
            if (!this instanceof User;) {
                return false;
            } else if (!super.equals(o)) {
                return false;
            } else {
                // 判断name是否相同
                Object this$name = this.name;
                Object other$name = other.name;
                if (this$name == null) {
                    if (other$name != null) {
                        return false;
                    }
                } else if (!this$name.equals(other$name)) {
                    return false;
                }
                // 判断level是否相同
                Object this$level = this.level;
                Object other$level = other.level;
                if (this$level == null) {
                    if (other$level != null) {
                        return false;
                    }
                } else if (!this$level.equals(other$level)) {
                    return false;
                }

                return true;
            }
        }
    }
    
    @Override
    public int hashCode() {
        int PRIME = 59;
        int result = super.hashCode();
        result = result * PRIME + (this.name == null ? 43 : $name.hashCode());
        result = result * PRIME + (this.level == null ? 43 : $level.hashCode());
        return result;
    }
}

(2)排除指定字段

​  可以排除指定字段不参与equal()、hashCode()的判断。

java
// 示例一:排除指定字段
@EqualsAndHashCode(exclude="age")
public class User extends Person{
    private String name;
    private String age;
}

// 示例二:排除多个字段
@EqualsAndHashCode(exclude={"address","age"})
public class User extends Person{
    private String name;
    private String age;
}

// 示例三:排除指定字段
public class User extends Person{
    @EqualsAndHashCode.Exclude private String name;
    private String age;
}

@Data

(1)在类上注释

​  使用@Data这一个注解等价于@Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor的集合。

java
@Data
public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
}
等价于 Java 源码
java
public class User {
    private String name;

    public String getName() {
        return this.name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @Override
    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof User)) {
            return false;
        } else {
            User other = (User)o;
            if (!this instanceof User) {
                return false;
            } else {
                Object this$name = this.getName();
                Object other$name = other.getName();
                if (this$name == null) {
                    if (other$name != null) {
                        return false;
                    }
                } else if (!this$name.equals(other$name)) {
                    return false;
                }

                return true;
            }
        }
    }

    @Override
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        return result;
    }

    @Override
    public String toString() {
        return "User(name=" + this.getName() + ")";
    }
}

(2)staticConstructor参数

java
// staticConstructor可以生成一个静态的构造参数
// 但是也不允许使用new User()构造对象了
@Data(staticConstructor="of")
public class User {
    private String name;
}

// 调用示例
val userName = User.of().getName();
// 提示错误
new User();

等价于 Java 源码:

java
public class User {
    private String name;
  
    public static User of() {
        return new User();
    }
    // 其他的省略...
}

构造方法

@AllArgsConstructor

java
@AllArgsConstructor
public class User {
   private String name;
   private String age;
   private String ss;
}

等价于 Java 源码:

java
public class User {
    private String name;
    private String age;

    public User(final String name, final String age, final String ss) {
        this.name = name;
        this.age = age;
    }
}

@NoArgsConstructor

java
@NoArgsConstructor
public class User {
   private String name;
   private String age;
   private String ss;
}

等价于 Java 源码:

java
public class User {
    private String name;
    private String age;

    public User() {
    }
}

@RequiredArgsConstructor

(1)根据@NonNull标记的属性生成对应的构造方法

java
@RequiredArgsConstructor
public class User {
   @NonNull private String name;
   private String age;

   private String ss;
}

等价于 Java 源码:

java
public class User {
    private @NonNull String name;
    private String age;
    private String ss;

    public User(final @NonNull String name) {
      if (name == null) {
         throw new NullPointerException("name is marked non-null but is null");
      } else {
         this.name = name;
      }
    }
}

(2)staticName参数

java
// staticName可以生成一个静态的构造参数,但是也不允许使用new User()构造对象了
@RequiredArgsConstructor(staticName="of")
public class User {
    private String name;
}
// 调用示例
val userName = User.of().setName("张三");
// 提示错误
new User();

等价于 Java 源码:

java
public class User {
    private String name;
  
    public static User of() {
        return new User();
    }
    // 其他的省略...
}

更多的注解

@val和@var

java
//val 定义final变量
val name = "张三";
// var 定义非final变量
var age = 14;

等价于 Java 源码:

java
final String name = "张三";

String age = 14;

@Builder

​  @Data、@Builder、@NoArgsConstructor、@AllArgsConstructor四个注解一般是一起使用的@Builder.Default用于设置默认值;使用@Singular注解可以对集合字段进行赋值,但集合字段名称必须以 s 结尾

java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Builder.Default
    private Integer age = 10;

    private String name;

    /**
     * 使用@Singular标注的参数名必须以 s 结尾
     * */
    @Singular
    private List<String> friends;

    public static void main(String[] args) {
       val user1 =  User.builder()
               .name("张三")
               .friend("李四")
               .friend("王五")
               .build();
       System.out.println(user1.toString());

       val user2 = new User();
       System.out.println(user2);
    }
}
// 输出:
User(age=10, name=张三, friends=[李四, 王五])
User(age=10, name=null, friends=null)

@Cleanup

​  @Cleanup会从注解的下一行开始生成一个try-finally关闭IO流@Cleanup("close") 的参数列表可以指定finally调用的方法,默认是"close"

java
public void testCleanUp() {
    try {
        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(new byte[] {'Y','e','s'});
        System.out.println(baos.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

等价的 Java 源码:

java
public void testCleanUp() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(new byte[]{'Y', 'e', 's'});
            System.out.println(baos.toString());
        } 
        finally {
            baos.close();// 可以指定调用的方法
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@SneakyThrows

(1)指定抛出异常

java
@SneakyThrows(IllegalAccessException.class)
public void testSneakyThrows() {
    throw new IllegalAccessException();
}

等价的 Java 源码

java
public void testSneakyThrows() {
    try {
        throw new IllegalAccessException();
    } catch (IllegalAccessException var2) {
        throw var2;{
    }
}

(2)不指定抛出异常(即捕获所有异常)

java
@SneakyThrows
public void testSneakyThrows() {
    throw new IllegalAccessException();
}

等价的 Java 源码:

java
public void testSneakyThrows() {
    try {
        throw new IllegalAccessException();
    } catch (java.lang.Throwable $ex) {
        throw lombok.Lombok.sneakyThrow($ex);
    }
}

@Synchronized

(1)不指定锁名

java
@Synchronized
public String synchronizedFormat() {
    return "张三";
}

等价的 Java 源码:

java
// Lombok生成了一个锁
private final Object $lock = new Object[0];

public String synchronizedFormat() {
    synchronized(this.$lock) {
            return "张三";
        }
    }
}

(2)指定锁名

java
private final Object readLock = new Object();

@Synchronized("readLock")
public String synchronizedFormat() {
    return "张三";
}

等价的 Java 源码

java
// 使用的是自定义的锁
private final Object readLock = new Object();

public String synchronizedFormat() {
    synchronized(this.readLock) {
        return "张三";
    }
}

@With

​  用于final修饰的属性前,一旦修改该final属性,就会生成一个新对象。

java
@Data
public class User {
    @With
    private final Integer no;
    
    public static void main(String[] args) {
        val user = new User(21);
        
        val newUSer= user.withNo(21);
        // 显示true,因为未修改
        System.out.println(newUSer == user); 
        
        val newUSer= user.withNo(12);
        // 显示false,因为已修改
        System.out.println(newUSer == user);
    }
}

等价于 Java 源码:

java
public class User {
    private final Integer no;

    public User withNo(final Integer no) {
        return this.no == no ? this : new User(no);{
    }
}

@NonNull

(1)在形参上使用

java
public void setName(@NonNull String name) {
    this.name = name;
}

等价于 Java 源码:

java
 public void setName(@NonNull String name) {
     if (name == null) {
     	throw new NullPointerException("name is marked non-null but is null");
     } 
     else {
    	 this.name = name;
     }
 }

(2)在属性上使用

java
public class User{
    @Setter @NonNull 
    private String name;
}

等价于 Java 源码:

java
public class User {
    private String name;

    public void setName(@NonNull String name) {
      if (name == null) {
         throw new NullPointerException("name is marked non-null but is null");
      } 
      else {
         this.name = name;
      }
    }
}

@Log

​  @Log是JDK自带的日志组件,可直接使用。若想使用@Slf4j还需要添加如下依赖:

xml
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.3</version>
</dependency>

@Value

(1)用法说明

- 会使得类变成final的 (即类变得不可变 且 不能被继承)
- 会使得没有声明访问权限的属性变为私有的
- 会使得属性变为final的,可以通过@NonFinal来标记某个属性不变成final
- 同时还会生成setter()、 getter()、 equals()、hashCode()、 toString()方法
- 还会生成一个全参数的构造方法

(2)用法示例

java
@Value
public class User {
   String name;
   @NonFinal  protected Integer age;
}
等价的 Java 源码
java
public final class User {
    private final String name;
    protected Integer age;

    public User(final String name, final Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public Integer getAge() {
        return this.age;
    }

    @Override
    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof User)) {
            return false;
        } else {
            User other = (User)o;
            Object this$age = this.getAge();
            Object other$age = other.getAge();
            if (this$age == null) {
                if (other$age != null) {
                    return false;
                }
            } else if (!this$age.equals(other$age)) {
                return false;
            }

            Object this$name = this.getName();
            Object other$name = other.getName();
            if (this$name == null) {
                if (other$name != null) {
                    return false;
                }
            } else if (!this$name.equals(other$name)) {
                return false;
            }

            return true;
        }
    }

    @Override
    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $age = this.getAge();
        result = result * 59 + ($age == null ? 43 : $age.hashCode());
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        return result;
    }

    @Override
    public String toString() {
        String var10000 = this.getName();
        return "User(name=" + var10000 + ", age=" + this.getAge() + ")";
    }
}

注意的问题

谨慎使用@Builder

​  若只在类上标注了 @Data@Builder 注解的时候,编译时,lombok 优化后的 Class 中会没有默认的构造方法

​  在反序列化的时候,没有默认构造方法就可能会报错,这里容易造成容易忽略的BUG。实例如下所示:

java
@Data
@Builder
public class BuilderDemo01 {

  private String name;

   public static void main(String[] args) throws JsonProcessingException {
      BuilderDemo01 demo01 = BuilderDemo01.builder().name("demo01").build();
      ObjectMapper mapper = new ObjectMapper();
      String json = mapper.writeValueAsString(demo01);
      BuilderDemo01 expectDemo01 = mapper.readValue(json, BuilderDemo01.class);
      System.out.println(expectDemo01.toString());
   }
}

@Data注解和继承

​  当Java子类和父类同时使用@Data注解时,可能导致equals()toString())不正确,因为在默认情况下@EqualsAndHashCode@ToString不会考虑父类属性。实例如下所示:

java
// Person类
@Data
public class Person {
    private String no;
}

// User类
@Data
public class User extends Person{
    private String name;
}

// 测试程序
public static void main(String[] args) {

        val user1 = new User();
        user1.setName("张三");
        user1.setNo("2");

        val user2 = new User();
        user2.setName("张三");
        user2.setNo("1");

        // user1和user2并不相等,但此时打印的是true
        System.out.println(user1.equals(user2));
}

​  修复此问题的方法很简单,只需使用 @Data时,在子类中添加如下注解,确保在比较时包含父类属性,以实现对称的equals方法。

java
@Data
@EqualsAndHashCode(callSuper=true)
public class User extends Person{
    private String name;
}

​  然后,在观察生成的User.class文件,发现加入callSuper=true参数后,就也会判定父类的属性是否equal了。

图片加载中... 请稍等(~ ̄▽ ̄)~

​  再次执行程序,发现打印的就是false了,问题已解决。

​  另外,除上述方法外,还可以使用 @Getter@Setter@ToString代替@Data,并且自定义equals()方法和hashCode()方法。

更新时间:

Released under the MIT License.