Lombok 这个开源项目提供了一系列注解简化 Java 开发,帮助生成模板代码。本文对 Lombok 提供的注解进行简单的介绍。

1、@Getter/@Setter

注解使用在类变量上,帮助生成默认的 getter/setter 方法。假设字段 foo 使用了这两个注解,则命名模式为:

  • 如果 foo 不是 boolean 类型,则方法会被命名为:getFoo()/setFoo();
  • 如果 foo 是 boolean 类型,则方法会被命名为:isFoo()/setFoo()。

访问级别默认都是 public,当然还可以通过 AccessLevel 来自定义,访问级别分为四种:PUBLICPROTECTEDPACKAGEPRIVATE

2、@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor

这三个注解使用在类级别上,会为类生成构造器。

  • @NoArgsConstructor 会生成一个没有参数的构造器,如果类有 final 修饰的属性,会抛出编译时异常 —— 除非使用了@NoArgsConstructor(force = true)
  • @RequiredArgsConstructor 会为所有用 final@NonNull 修饰的属性生成一个构造器;
  • @AllArgsConstructor 顾名思义会生成一个包含所有类变量的构造器。

3、@ToString

这个注解使用在类级别上,会为类生成 toString 方法,它有两个属性:

  • callSuper:会调用父类的 toString 方法;
  • includeFieldNames:在 toString 方法中会输出字段名。

4、@EqualsAndHashCode

《Effective Java》第9条建议介绍过,实现 equals 方法的同时必须实现 hashCode 方法 —— 如果不实现 hashCode,这个类就不能HashMap 等基于哈希的集合类正常工作;然而编写一个正确的 hashCode 方法是有一定难度的。

  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof EqualsAndHashCodeExample)) return false;
    EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (Double.compare(this.score, other.score) != 0) return false;
    if (!Arrays.deepEquals(this.tags, other.tags)) return false;
    return true;
  }
  
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.score);
    result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.tags);
    return result;
  }

这个注解使用在类级别上,会为类生成 equalshashCode 方法,极大地简化了程序员的工作。

5、@Cleanup

这个注解会自动调用类的 close() 方法和那一系列 try-catch 代码,用在方法的临时变量上 —— 比如输入输出流。程序员经常忘记在使用完毕之后调用 close() 方法导致内存泄漏的风险。

  • value:如果没有 close() 方法,可以在 value 中指明方法名。
public class CleanupExample {
  public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream(args[0]); // 可用 @Cleanup 注解
    try {
      OutputStream out = new FileOutputStream(args[1]); // 可用 @Cleanup 注解
      try {
        byte[] b = new byte[10000];
        while (true) {
          int r = in.read(b);
          if (r == -1) break;
          out.write(b, 0, r);
        }
      } finally {
        if (out != null) {
          out.close();
        }
      }
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

6、@NonNull

这个注解可以使用在构造器或是方法的变量上。它会为这个变量生成一段非空检查的代码。

public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(Person person) { // @NonNull 可以用于 person
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

7、@Data

这个注解用于类级别上,是一系列注解的集合:等于同时在类级别使用了 @ToString@EqualsAndHashCode@RequiredArgsConstructor,为所有类变量上加上 @Getter, 为所有非 final 变量加上 @Setter

8、@Value

@Data 注解类似,不同的地方在于它为类和所有类变量加上了 final 修饰 - 除非类变量已经有 @NonFinal 修饰了。

9、@Builder

这个注解使用在类级别上,它实现了《Effective Java》书中的第2条建议或者说是 Builder 模式

@Builder
public class BuilderExample {
  private String name;
  private int age;
}

相当于:

public class BuilderExample {
  private String name;
  private int age;
  
  BuilderExample(String name, int age, Set<String> occupations) {
    this.name = name;
    this.age = age;
    this.occupations = occupations;
  }
  
  public static BuilderExampleBuilder builder() {
    return new BuilderExampleBuilder();
  }
  
  public static class BuilderExampleBuilder {
    private String name;
    private int age;
    private java.util.ArrayList<String> occupations;
    
    BuilderExampleBuilder() {
    }
    
    public BuilderExampleBuilder name(String name) {
      this.name = name;
      return this;
    }
    
    public BuilderExampleBuilder age(int age) {
      this.age = age;
      return this;
    }
  }
}