Lombok

Lombok

Lomlook 官方文档

Lombok注解说明

  • val:用在局部变量前面,相当于将变量声明为final
  • @NonNull:给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException)
  • @Cleanup:自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流
  • @Getter/@Setter:用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围
  • @ToString:用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude=”id”)排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性
  • @EqualsAndHashCode:用在类上,自动生成equals方法和hashCode方法
  • @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有@NonNull属性作为参数的构造函数,如果指定staticName = “of”参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多
  • @Data:注解在类上,相当于同时使用了@ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstrutor这些注解,对于POJO类十分有用
  • @Value:用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
  • @Builder:用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();更多说明参考Builder
  • @SneakyThrows:自动抛受检异常,而无需显式在方法上使用throws语句
  • @Synchronized:用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock$LOCK,而java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误
  • @Getter(lazy=true):可以替代经典的Double Check Lock样板代码
  • @Log:根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类
    • @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
    • @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
    • @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
    • @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
    • @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
    • @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
  • experimental  试验性
    • @Accessors 一个更流畅的 API,适用于 getter 和 setter。
    • @ExtensionMethod 烦人的 API?自己修复:向现有类型添加新方法!
    • @ExtensionMethod 烦人的 API?自己修复:向现有类型添加新方法!
    • @FieldDefaults 21 世纪的新默认字段修饰符。
    • @Delegate 不要失去你的构图。
    • onMethod= / onConstructor= / onParam= 我们听说你喜欢注释,所以我们在你的注释中放了注释,这样你就可以在注释时进行注释。
    • @UtilityClass 实用性,实用性,湿度!大众实用类。
    • @Helper 烦人的 API?自己修复:向现有类型添加新方法!
    • @FieldNameConstants 字段名称的字符串常量。
    • @SuperBuilder 父类建造者
    • @Tolerate 使lombok忽略现有方法或构造函数。
    • @Jacksonized Jack序列化方法
    • @StandardException 标准的异常类处理.

使用方法

@val

public String example() {
  val example = new ArrayList<String>();
  example.add("Hello, World!");
  val foo = example.get(0);
  return foo.toLowerCase();
 }
  //=>相当于如下
 public String example() {
    final ArrayList<String> example = new ArrayList<String>();
    example.add("Hello, World!");
    final String foo = example.get(0);
    return foo.toLowerCase();
  }
  

@NonNull

您可以@NonNull在记录组件、方法或构造函数的参数上使用。这将导致 lombok 为您生成一个空检查语句。

public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

//=>等同于
public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person is marked @NonNull but is null");
    }
    this.name = person.getName();
  }
}

@Cleanup

可以使用@Cleanup确保在代码执行路径退出当前范围之前自动清理给定资源。您可以通过使用这样的注释来注释任何局部变量声明来做到这@Cleanup一点:
@Cleanup InputStream in = new FileInputStream("some/file");
结果,在您所在的范围的末尾,in.close()被调用。此调用保证通过 try/finally 构造运行。看看下面的例子,看看它是如何工作的。

如果您要清理的对象类型没有close()方法,但有其他一些无参数方法,则可以像这样指定此方法的名称:
@Cleanup("dispose") org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent, 0);
默认情况下,清理方法假定为close(). 不能通过 调用带有 1 个或多个参数的清理方法@Cleanup

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    @Cleanup InputStream in = new FileInputStream(args[0]);
    @Cleanup OutputStream out = new FileOutputStream(args[1]);
    byte[] b = new byte[10000];
    while (true) {
      int r = in.read(b);
      if (r == -1) break;
      out.write(b, 0, r);
    }
  }
}

//=>等同于

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream(args[0]);
    try {
      OutputStream out = new FileOutputStream(args[1]);
      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();
      }
    }
  }
}

@Getter 和 @Setter

可以使用@Getter和/或注释任何字段@Setter,让 lombok 自动生成默认的 getter/setter。

默认 getter 仅返回该字段,并getFoo在该字段被调用foo(或isFoo该字段的类型为boolean)时命名。setFoo如果该字段被调用foo、返回void并采用与该字段相同类型的 1 个参数,则会命名默认设置器。它只是将字段设置为此值。

public除非您明确指定AccessLevel,否则 生成的 getter/setter 方法将是,如下面的示例所示。法律访问级别PUBLICPROTECTEDPACKAGE,和PRIVATE

您还可以在类上添加@Getter和/或@Setter注释。在这种情况下,就好像您使用注释对该类中的所有非静态字段进行了注释。

您始终可以使用特殊AccessLevel.NONE访问级别为任何字段手动禁用 getter/setter 生成。这使您可以覆盖类上的@Getter,@Setter@Data注解的行为。

要在生成的方法上添加注释,您可以使用onMethod=@__({@AnnotationsHere}); 要将注释放在生成的 setter 方法的唯一参数上,您可以使用onParam=@__({@AnnotationsHere}). 不过要小心!这是一个实验性功能。有关更多详细信息,请参阅onX功能的文档。

lombok v1.12.0 中的新功能:字段上的 javadoc 现在将被复制到生成的 getter 和 setter。通常情况下,所有的文字被复制,且@return移动到吸气剂,而@param线移动的制定者。移动的意思是:从字段的 javadoc 中删除。也可以为每个 getter/setter 定义唯一的文本。为此,您创建一个名为GETTER和/或SETTER. 节是 javadoc 中包含 2 个或更多破折号的一行,然后是文本 'GETTER' 或 'SETTER',后跟 2 个或更多破折号,该行没有其他任何内容。如果您使用部分,@return并且@param不再进行该部分的剥离(将@return@param行移到该部分中)。

public class GetterSetterExample {
  /**
   * Age of the person. Water is wet.
   * 
   * @param age New value for this person's age. Sky is blue.
   * @return The current value of this person's age. Circles are round.
   */
  @Getter @Setter private int age = 10;
  
  /**
   * Name of the person.
   * -- SETTER --
   * Changes the name of this person.
   * 
   * @param name The new value.
   */
  @Setter(AccessLevel.PROTECTED) private String name;
  
  @Override public String toString() {
    return String.format("%s (age: %d)", name, age);
  }
}

// 等同于


public class GetterSetterExample {
  /**
   * Age of the person. Water is wet.
   */
  private int age = 10;

  /**
   * Name of the person.
   */
  private String name;
  
  @Override public String toString() {
    return String.format("%s (age: %d)", name, age);
  }
  
  /**
   * Age of the person. Water is wet.
   *
   * @return The current value of this person's age. Circles are round.
   */
  public int getAge() {
    return age;
  }
  
  /**
   * Age of the person. Water is wet.
   *
   * @param age New value for this person's age. Sky is blue.
   */
  public void setAge(int age) {
    this.age = age;
  }
  
  /**
   * Changes the name of this person.
   *
   * @param name The new value.
   */
  protected void setName(String name) {
    this.name = name;
  }
}

@ToString

@ToString
public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  @ToString.Exclude private int id;
  
  public String getName() {
    return this.name;
  }
  
  @ToString(callSuper=true, includeFieldNames=true)
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

// 等同于

public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.name;
  }
  
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
    
    @Override public String toString() {
      return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
    }
  }
  
  @Override public String toString() {
    return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
  }
}

@EqualsAndHashCode

任何类定义都可以使用注释@EqualsAndHashCode来让 lombok 生成equals(Object other)hashCode()方法的实现。

@EqualsAndHashCode
public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  @EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
  private String[] tags;
  @EqualsAndHashCode.Exclude private int id;
  
  public String getName() {
    return this.name;
  }
  
  @EqualsAndHashCode(callSuper=true)
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

Vanilla Java


public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.name;
  }
  
  @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;
  }
  
  protected boolean canEqual(Object other) {
    return other instanceof EqualsAndHashCodeExample;
  }
  
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
    
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Square)) return false;
      Square other = (Square) o;
      if (!other.canEqual((Object)this)) return false;
      if (!super.equals(o)) return false;
      if (this.width != other.width) return false;
      if (this.height != other.height) return false;
      return true;
    }
    
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + super.hashCode();
      result = (result*PRIME) + this.width;
      result = (result*PRIME) + this.height;
      return result;
    }
    
    protected boolean canEqual(Object other) {
      return other instanceof Square;
    }
  }
}

@NoArgsConstructor、@RequiredArgsConstructor、@AllArgsConstructor

@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  
  @NoArgsConstructor
  public static class NoArgsExample {
    @NonNull private String field;
  }
}

Vanilla Java

public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  
  private ConstructorExample(T description) {
    if (description == null) throw new NullPointerException("description");
    this.description = description;
  }
  
  public static <T> ConstructorExample<T> of(T description) {
    return new ConstructorExample<T>(description);
  }
  
  @java.beans.ConstructorProperties({"x", "y", "description"})
  protected ConstructorExample(int x, int y, T description) {
    if (description == null) throw new NullPointerException("description");
    this.x = x;
    this.y = y;
    this.description = description;
  }
  
  public static class NoArgsExample {
    @NonNull private String field;
    
    public NoArgsExample() {
    }
  }
}

@Data

@Data是捆绑的特征的方便快捷方式注释@ToString@EqualsAndHashCode@Getter/@Setter@RequiredArgsConstructor在一起:


@Data public class DataExample {
  private final String name;
  @Setter(AccessLevel.PACKAGE) private int age;
  private double score;
  private String[] tags;
  
  @ToString(includeFieldNames=true)
  @Data(staticConstructor="of")
  public static class Exercise<T> {
    private final String name;
    private final T value;
  }
}

Vanilla Java


public class DataExample {
  private final String name;
  private int age;
  private double score;
  private String[] tags;
  
  public DataExample(String name) {
    this.name = name;
  }
  
  public String getName() {
    return this.name;
  }
  
  void setAge(int age) {
    this.age = age;
  }
  
  public int getAge() {
    return this.age;
  }
  
  public void setScore(double score) {
    this.score = score;
  }
  
  public double getScore() {
    return this.score;
  }
  
  public String[] getTags() {
    return this.tags;
  }
  
  public void setTags(String[] tags) {
    this.tags = tags;
  }
  
  @Override public String toString() {
    return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  }
  
  protected boolean canEqual(Object other) {
    return other instanceof DataExample;
  }
  
  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof DataExample)) return false;
    DataExample other = (DataExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }
  
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.getScore());
    result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
    result = (result*PRIME) + this.getAge();
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
    return result;
  }
  
  public static class Exercise<T> {
    private final String name;
    private final T value;
    
    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }
    
    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }
    
    public String getName() {
      return this.name;
    }
    
    public T getValue() {
      return this.value;
    }
    
    @Override public String toString() {
      return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
    }
    
    protected boolean canEqual(Object other) {
      return other instanceof Exercise;
    }
    
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Exercise)) return false;
      Exercise<?> other = (Exercise<?>) o;
      if (!other.canEqual((Object)this)) return false;
      if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
      if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
      return true;
    }
    
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
      result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
      return result;
    }
  }
}

@Value

@Value是 的不可变变体@Data;所有字段由privatefinal默认情况下,也不会产生setter方法。类本身也是final默认创建的,因为不可变性不是可以强加给子类的东西。像@Data,有用的toString()equals()并且hashCode()也被生成的方法,每个字段得到的吸气剂的方法,以及覆盖每个参数(除了一个构造final了在字段声明初始化字段)也被产生。

在实践中,@Value是: 的简写final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter,除了明确包含任何相关方法的实现仅意味着不会生成该部分并且不会发出警告。比如自己写的toString,不会出错,lombok也不会生成toString. 此外,任何显式构造函数,无论参数列表如何,都意味着 lombok 不会生成构造函数。如果您确实希望 lombok 生成全参数构造函数,请添加@AllArgsConstructor到类中。请注意,如果 @Builder@Value 都在一个类上,@Builder 想要创建的包私有 allargs 构造函数“胜过”@Value 想要创建的公共构造函数。您可以标记任何构造函数或方法以@lombok.experimental.Tolerate在 lombok 中隐藏它们。

可以使用字段上的显式访问级别或使用@NonFinalor@PackagePrivate注释来覆盖 final-by-default 和 private-by-default 行为。@NonFinal也可以用于类以删除 final 关键字。
可以@Value通过显式使用该注释来覆盖组成的任何“部分”的任何默认行为。

@Value public class ValueExample {
  String name;
  @With(AccessLevel.PACKAGE) @NonFinal int age;
  double score;
  protected String[] tags;
  
  @ToString(includeFieldNames=true)
  @Value(staticConstructor="of")
  public static class Exercise<T> {
    String name;
    T value;
  }
}

Vanilla Java

public final class ValueExample {
  private final String name;
  private int age;
  private final double score;
  protected final String[] tags;
  
  @java.beans.ConstructorProperties({"name", "age", "score", "tags"})
  public ValueExample(String name, int age, double score, String[] tags) {
    this.name = name;
    this.age = age;
    this.score = score;
    this.tags = tags;
  }
  
  public String getName() {
    return this.name;
  }
  
  public int getAge() {
    return this.age;
  }
  
  public double getScore() {
    return this.score;
  }
  
  public String[] getTags() {
    return this.tags;
  }
  
  @java.lang.Override
  public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof ValueExample)) return false;
    final ValueExample other = (ValueExample)o;
    final Object this$name = this.getName();
    final Object other$name = other.getName();
    if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }
  
  @java.lang.Override
  public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final Object $name = this.getName();
    result = result * PRIME + ($name == null ? 43 : $name.hashCode());
    result = result * PRIME + this.getAge();
    final long $score = Double.doubleToLongBits(this.getScore());
    result = result * PRIME + (int)($score >>> 32 ^ $score);
    result = result * PRIME + Arrays.deepHashCode(this.getTags());
    return result;
  }
  
  @java.lang.Override
  public String toString() {
    return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
  }
  
  ValueExample withAge(int age) {
    return this.age == age ? this : new ValueExample(name, age, score, tags);
  }
  
  public static final class Exercise<T> {
    private final String name;
    private final T value;
    
    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }
    
    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }
    
    public String getName() {
      return this.name;
    }
    
    public T getValue() {
      return this.value;
    }
    
    @java.lang.Override
    public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof ValueExample.Exercise)) return false;
      final Exercise<?> other = (Exercise<?>)o;
      final Object this$name = this.getName();
      final Object other$name = other.getName();
      if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
      final Object this$value = this.getValue();
      final Object other$value = other.getValue();
      if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
      return true;
    }
    
    @java.lang.Override
    public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      final Object $name = this.getName();
      result = result * PRIME + ($name == null ? 43 : $name.hashCode());
      final Object $value = this.getValue();
      result = result * PRIME + ($value == null ? 43 : $value.hashCode());
      return result;
    }
    
    @java.lang.Override
    public String toString() {
      return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
    }
  }
}

@Builder

@Builder
public class BuilderExample {
  @Builder.Default private long created = System.currentTimeMillis();
  private String name;
  private int age;
  @Singular private Set<String> occupations;
}

Vanilla Java


public class BuilderExample {
  private long created;
  private String name;
  private int age;
  private Set<String> occupations;
  
  BuilderExample(String name, int age, Set<String> occupations) {
    this.name = name;
    this.age = age;
    this.occupations = occupations;
  }
  
  private static long $default$created() {
    return System.currentTimeMillis();
  }
  
  public static BuilderExampleBuilder builder() {
    return new BuilderExampleBuilder();
  }
  
  public static class BuilderExampleBuilder {
    private long created;
    private boolean created$set;
    private String name;
    private int age;
    private java.util.ArrayList<String> occupations;
    
    BuilderExampleBuilder() {
    }
    
    public BuilderExampleBuilder created(long created) {
      this.created = created;
      this.created$set = true;
      return this;
    }
    
    public BuilderExampleBuilder name(String name) {
      this.name = name;
      return this;
    }
    
    public BuilderExampleBuilder age(int age) {
      this.age = age;
      return this;
    }
    
    public BuilderExampleBuilder occupation(String occupation) {
      if (this.occupations == null) {
        this.occupations = new java.util.ArrayList<String>();
      }
      
      this.occupations.add(occupation);
      return this;
    }
    
    public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
      if (this.occupations == null) {
        this.occupations = new java.util.ArrayList<String>();
      }

      this.occupations.addAll(occupations);
      return this;
    }
    
    public BuilderExampleBuilder clearOccupations() {
      if (this.occupations != null) {
        this.occupations.clear();
      }
      
      return this;
    }

    public BuilderExample build() {
      // complicated switch statement to produce a compact properly sized immutable set omitted.
      Set<String> occupations = ...;
      return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
    }
    
    @java.lang.Override
    public String toString() {
      return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
    }
  }
}

@SneakyThrows

@SneakyThrows可用于偷偷地抛出已检查的异常,而无需在方法的throws子句中实际声明。

public class SneakyThrowsExample implements Runnable {
  @SneakyThrows(UnsupportedEncodingException.class)
  public String utf8ToString(byte[] bytes) {
    return new String(bytes, "UTF-8");
  }
  
  @SneakyThrows
  public void run() {
    throw new Throwable();
  }
}

Vanilla Java

public class SneakyThrowsExample implements Runnable {
  public String utf8ToString(byte[] bytes) {
    try {
      return new String(bytes, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw Lombok.sneakyThrow(e);
    }
  }
  
  public void run() {
    try {
      throw new Throwable();
    } catch (Throwable t) {
      throw Lombok.sneakyThrow(t);
    }
  }
}

@Synchronized

@Synchronizedsynchronized方法修饰符的一个更安全的变体。

public class SynchronizedExample {
  private final Object readLock = new Object();
  
  @Synchronized
  public static void hello() {
    System.out.println("world");
  }
  
  @Synchronized
  public int answerToLife() {
    return 42;
  }
  
  @Synchronized("readLock")
  public void foo() {
    System.out.println("bar");
  }
}

Vanilla Java


public class SynchronizedExample {
  private static final Object $LOCK = new Object[0];
  private final Object $lock = new Object[0];
  private final Object readLock = new Object();
  
  public static void hello() {
    synchronized($LOCK) {
      System.out.println("world");
    }
  }
  
  public int answerToLife() {
    synchronized($lock) {
      return 42;
    }
  }
  
  public void foo() {
    synchronized(readLock) {
      System.out.println("bar");
    }
  }
}

@Wither

不可变属性的 setter 的下一个最佳替代方法是构造对象的克隆,但为该字段使用新值。生成此克隆的方法正是@With生成的withFieldName(newValue)方法:除了关联字段的新值之外,生成克隆的方法。

例如,如果您 create public class Point { private final int x, y; },setter 没有意义,因为这些字段是最终的。@With可以withX(int newXValue)为您生成一个方法,该方法将返回一个新点,该点具有为 提供的值x和相同的值y

public class WithExample {
  @With(AccessLevel.PROTECTED) @NonNull private final String name;
  @With private final int age;
  
  public WithExample(String name, int age) {
    if (name == null) throw new NullPointerException();
    this.name = name;
    this.age = age;
  }
}

Vanilla Java


public class WithExample {
  private @NonNull final String name;
  private final int age;

  public WithExample(String name, int age) {
    if (name == null) throw new NullPointerException();
    this.name = name;
    this.age = age;
  }

  protected WithExample withName(@NonNull String name) {
    if (name == null) throw new java.lang.NullPointerException("name");
    return this.name == name ? this : new WithExample(name, age);
  }

  public WithExample withAge(int age) {
    return this.age == age ? this : new WithExample(name, age);
  }
}

@Getter(lazy=true)

你可以让 lombok 生成一个 getter,它会在第一次调用这个 getter 时计算一次值,然后缓存它。如果计算该值需要大量 CPU 或该值需要大量内存,这会很有用。

public class GetterLazyExample {
  @Getter(lazy=true) private final double[] cached = expensive();
  
  private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
      result[i] = Math.asin(i);
    }
    return result;
  }
}

Vanilla Java

public class GetterLazyExample {
  private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
  
  public double[] getCached() {
    java.lang.Object value = this.cached.get();
    if (value == null) {
      synchronized(this.cached) {
        value = this.cached.get();
        if (value == null) {
          final double[] actualValue = expensive();
          value = actualValue == null ? this.cached : actualValue;
          this.cached.set(value);
        }
      }
    }
    return (double[])(value == this.cached ? null : value);
  }
  
  private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
      result[i] = Math.asin(i);
    }
    return result;
  }
}

@Log

记录器已命名log,字段的类型取决于您选择的记录器。

@CommonsLog

private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

@Flogger

private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();

@JBossLog

private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);

@Log

private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

@Log4j

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);

@Log4j2

private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

@Slf4j

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

@XSlf4j

private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

@CustomLog

private static final *com.foo.your.Logger* log = *com.foo.your.LoggerFactory.createYourLogger*(LogExample.class);
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;

@Log
public class LogExample {
  
  public static void main(String... args) {
    log.severe("Something's wrong here");
  }
}

@Slf4j
public class LogExampleOther {
  
  public static void main(String... args) {
    log.error("Something else is wrong here");
  }
}

@CommonsLog(topic="CounterLog")
public class LogExampleCategory {

  public static void main(String... args) {
    log.error("Calling the 'CounterLog' with a message");
  }
}

Vanilla Java


public class LogExample {
  private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  
  public static void main(String... args) {
    log.severe("Something's wrong here");
  }
}

public class LogExampleOther {
  private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
  
  public static void main(String... args) {
    log.error("Something else is wrong here");
  }
}

public class LogExampleCategory {
  private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");

  public static void main(String... args) {
    log.error("Calling the 'CounterLog' with a message");
  }
}

实验功能

实验性功能在您的常规 lombok 安装中可用,但不如 lombok 的主要功能得到强大的支持。特别是,实验功能:

  • 没有测试以及核心功能。
  • 不要像核心功能一样快速修复错误。
  • 如果我们找到一种不同的、更好的方法来解决相同的问题,可能会有一些 API 会发生变化。
  • 如果该功能太难以支持或没有破坏足够的样板,则可能会完全消失。

var

var工作方式与 完全相同val,只是局部变量标记为final

var x = "Hello"; x = Color.RED;

@Accessors

用于 getter 和 setter 的更流畅的 API。


@Accessors(fluent = true)
public class AccessorsExample {
  @Getter @Setter
  private int age = 10;
}

class PrefixExample {
  @Accessors(prefix = "f") @Getter
  private String fName = "Hello, World!";
}


//等同于

public class AccessorsExample {
  private int age = 10;
  
  public int age() {
    return this.age;
  }
  
  public AccessorsExample age(final int age) {
    this.age = age;
    return this;
  }
}

class PrefixExample {
  private String fName = "Hello, World!";
  
  public String getName() {
    return this.fName;
  }
}

@ExtensionMethod

实验性的,因为:

  • 对代码风格的影响很大。
  • 真的很想提供实用方法来扩展公共类,但到目前为止 lombok 没有一个很好的分发方法来处理这种运行时依赖项。
  • 影响相当多的 eclipse,并且自动完成 ed 在 netbeans 中还不起作用。
  • @ExtensionMethod 应该在方法上合法吗?它应该在包裹上合法吗?
  • 此功能的相关错误比我们希望的要多,并且是一个很大的维护负担。
@ExtensionMethod({java.util.Arrays.class, Extensions.class})
public class ExtensionMethodExample {
  public String test() {
    int[] intArray = {5, 3, 8, 2};
    intArray.sort();
    
    String iAmNull = null;
    return iAmNull.or("hELlO, WORlD!".toTitleCase());
  }
}

class Extensions {
  public static <T> T or(T obj, T ifNull) {
    return obj != null ? obj : ifNull;
  }
  
  public static String toTitleCase(String in) {
    if (in.isEmpty()) return in;
    return "" + Character.toTitleCase(in.charAt(0)) +
        in.substring(1).toLowerCase();
  }
}

Vanilla Java

public class ExtensionMethodExample {
  public String test() {
    int[] intArray = {5, 3, 8, 2};
    java.util.Arrays.sort(intArray);
    
    String iAmNull = null;
    return Extensions.or(iAmNull, Extensions.toTitleCase("hELlO, WORlD!"));
  }
}

class Extensions {
  public static <T> T or(T obj, T ifNull) {
    return obj != null ? obj : ifNull;
  }
  
  public static String toTitleCase(String in) {
    if (in.isEmpty()) return in;
    return "" + Character.toTitleCase(in.charAt(0)) +
        in.substring(1).toLowerCase();
  }
}

@FieldDefaults

所述@FieldDefaults注释可以添加一个访问修饰符(publicprivate,或protected在带注释的类或枚举)到每个字段。它还可以添加final到带注释的类或枚举中的每个字段。

要添加final到每个(实例)字段,请使用@FieldDefaults(makeFinal=true). 任何必须保持非最终的非最终字段都可以用@NonFinal(也在lombok.experimental包中)进行注释。

要将访问修饰符添加到每个(实例)字段,请使用@FieldDefaults(level=AccessLevel.PRIVATE). 任何还没有访问修饰符的字段(即任何看起来像包私有访问的字段)都将更改为具有适当的访问修饰符。任何必须保持包私有的包私有字段都可以注释@PackagePrivate(也在lombok.experimental包中)。


@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE)
public class FieldDefaultsExample {
  public final int a;
  int b;
  @NonFinal int c;
  @PackagePrivate int d;
  
  FieldDefaultsExample() {
    a = 0;
    b = 0;
    d = 0;
  }
}

Vanilla Java


public class FieldDefaultsExample {
  public final int a;
  private final int b;
  private int c;
  final int d;
  
  FieldDefaultsExample() {
    a = 0;
    b = 0;
    d = 0;
  }
}

@Delegate

可以使用任何字段或无参数方法进行注释,@Delegate以让 lombok 生成将调用转发到此字段(或调用此方法的结果)的委托方法。

Lombok 委托public字段类型(或方法的返回类型)的所有方法及其超类型的方法,但在java.lang.Object.

您可以将任意数量的类传递到@Delegate注释的types参数中。如果这样做,lombok 将委托public这些类型(及其超类型,除了java.lang.Object)中的所有方法,而不是查看字段/方法的类型。

所有Object属于计算类型的公共非方法都会被复制,无论您是否还为这些方法编写了实现。这将因此导致重复的方法错误。您可以通过使用@Delegate(excludes=SomeType.class)参数排除被排除类型及其超类型中的所有公共方法来避免这些。

要非常精确地控制委托和不委托的内容,请编写带有方法签名的私有内部接口,然后将这些私有内部接口指定为@Delegate(types=PrivateInnerInterfaceWithIncludesList.class, excludes=SameForExcludes.class).


public class DelegationExample {
  private interface SimpleCollection {
    boolean add(String item);
    boolean remove(Object item);
  }
  
  @Delegate(types=SimpleCollection.class)
  private final Collection<String> collection = new ArrayList<String>();
}


class ExcludesDelegateExample {
  long counter = 0L;
  
  private interface Add {
    boolean add(String x);
    boolean addAll(Collection<? extends String> x);
  }
  
  @Delegate(excludes=Add.class)
  private final Collection<String> collection = new ArrayList<String>();
  
  public boolean add(String item) {
    counter++;
    return collection.add(item);
  }
  
  public boolean addAll(Collection<? extends String> col) {
    counter += col.size();
    return collection.addAll(col);
  }
}

Vanilla Java


public class DelegationExample {
  private interface SimpleCollection {
    boolean add(String item);
    boolean remove(Object item);
  }
  
  private final Collection<String> collection = new ArrayList<String>();
  
  @java.lang.SuppressWarnings("all")
  public boolean add(final java.lang.String item) {
    return this.collection.add(item);
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean remove(final java.lang.Object item) {
    return this.collection.remove(item);
  }
}

class ExcludesDelegateExample {
  long counter = 0L;
  
  private interface Add {
    boolean add(String x);
    boolean addAll(Collection<? extends String> x);
  }
  
  private final Collection<String> collection = new ArrayList<String>();
  
  public boolean add(String item) {
    counter++;
    return collection.add(item);
  }
  
  public boolean addAll(Collection<? extends String> col) {
    counter += col.size();
    return collection.addAll(col);
  }
  
  @java.lang.SuppressWarnings("all")
  public int size() {
    return this.collection.size();
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean isEmpty() {
    return this.collection.isEmpty();
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean contains(final java.lang.Object arg0) {
    return this.collection.contains(arg0);
  }
  
  @java.lang.SuppressWarnings("all")
  public java.util.Iterator<java.lang.String> iterator() {
    return this.collection.iterator();
  }
  
  @java.lang.SuppressWarnings("all")
  public java.lang.Object[] toArray() {
    return this.collection.toArray();
  }
  
  @java.lang.SuppressWarnings("all")
  public <T extends .java.lang.Object>T[] toArray(final T[] arg0) {
    return this.collection.<T>toArray(arg0);
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean remove(final java.lang.Object arg0) {
    return this.collection.remove(arg0);
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean containsAll(final java.util.Collection<?> arg0) {
    return this.collection.containsAll(arg0);
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean removeAll(final java.util.Collection<?> arg0) {
    return this.collection.removeAll(arg0);
  }
  
  @java.lang.SuppressWarnings("all")
  public boolean retainAll(final java.util.Collection<?> arg0) {
    return this.collection.retainAll(arg0);
  }
  
  @java.lang.SuppressWarnings("all")
  public void clear() {
    this.collection.clear();
  }
}

onMethod= / onConstructor= / onParam=

此功能被认为是“解决方法状态”——它的存在是为了让 lombok 的用户可以在没有此功能的情况下无论如何都可以访问它。如果我们找到更好的方法来实现这个特性,或者未来某个 java 版本引入了替代策略,这个特性可以在没有合理的弃用期的情况下消失。此外,此功能可能不适用于 javac 的未来版本。自行决定使用。

大多数使 lombok 生成方法或构造函数的注释也可以配置为使 lombok 在生成的代码中的元素上放置自定义注释。

@Getter, @Setter, 并@Wither支持该onMethod选项,该选项会将列出的注释放在生成的方法上。

@AllArgsConstructor, @NoArgsConstructor, 并@RequiredArgsConstructor支持onConstructor将列出的注释放在生成的构造函数上的选项。

@Setter@Wither支持onParam此外onMethod;列出的注释将放在生成的方法具有的唯一参数上。@EqualsAndHashCode还支持onParam;列出的注释将放置在生成的equals方法以及任何生成的canEqual方法的单个参数上。

import javax.inject.Inject;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.validation.constraints.Max;

@AllArgsConstructor(onConstructor=@__(@Inject))
public class OnXExample {
//  @Getter(onMethod=@__({@Id, @Column(name="unique-id")})) //JDK7
//  @Setter(onParam=@__(@Max(10000))) //JDK7
  @Getter(onMethod_={@Id, @Column(name="unique-id")}) //JDK8
  @Setter(onParam_=@Max(10000)) //JDK8
  private long unid;
}

Vanilla Java

import javax.inject.Inject;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.validation.constraints.Max;

public class OnXExample {
  private long unid;

  @Inject
  public OnXExample(long unid) {
    this.unid = unid;
  }

  @Id @Column(name="unique-id")
  public long getUnid() {
    return unid;
  }

  public void setUnid(@Max(10000) long unid) {
    this.unid = unid;
  }
}

@UtilityClass

实用程序类是一个只是函数命名空间的类。它的实例不能存在,它的所有成员都是静态的。例如,java.lang.Mathjava.util.Collections是众所周知的实用程序类。这个注解会自动将注解的类变成一个类。

@UtilityClass
public class UtilityClassExample {
  private final int CONSTANT = 5;

  public int addSomething(int in) {
    return in + CONSTANT;
  }
}

Vanilla Java

public final class UtilityClassExample {
  private static final int CONSTANT = 5;

  private UtilityClassExample() {
    throw new java.lang.UnsupportedOperationException("This is a utility class and cannot be instantiated");
  }

  public static int addSomething(int in) {
    return in + CONSTANT;
  }
}

@Helper

import lombok.experimental.Helper;

public class HelperExample {
  int someMethod(int arg1) {
    int localVar = 5;

    @Helper class Helpers {
      int helperMethod(int arg) {
        return arg + localVar;
      }
    }

    return helperMethod(10);
  }
}

Vanilla Java


public class HelperExample {
  int someMethod(int arg1) {
    int localVar = 5;

    class Helpers {
      int helperMethod(int arg) {
        return arg + localVar;
      }
    }
    Helpers $Helpers = new Helpers();

    return $Helpers.helperMethod(10);
  }
}

@FieldNameConstants


import lombok.experimental.FieldNameConstants;
import lombok.AccessLevel;

@FieldNameConstants
public class FieldNameConstantsExample {
  private final String iAmAField;
  private final int andSoAmI;
  @FieldNameConstants.Exclude private final int asAmI;
}

Vanilla Java

public class FieldNameConstantsExample {
  private final String iAmAField;
  private final int andSoAmI;
  private final int asAmI;
  
  public static final class Fields {
    public static final String iAmAField = "iAmAField";
    public static final String andSoAmI = "andSoAmI";
  }
}

@SuperBuilder

普通的Builder无法建造父类,这个可以实现

@Tolerate


import lombok.experimental.Tolerate;

public class TolerateExample {
    @Setter
    private Date date;

    @Tolerate
    public void setDate(String date) {
        this.date = Date.valueOf(date);
    }
}

Vanilla Java

public class TolerateExample {
    @Setter
    private Date date;

    public void setDateFromString(String date) {
        this.date = Date.valueOf(date);
    }
}

@Jacksonized

@Jacksonized注释的英文一个附加注释的状语从句:。它会自动配置生成的构建器类以供jackson的反序列化使用。它仅在存在于也有一个或一个的上下文中时才有效;否则会发出警告。 @Builder@SuperBuilder@Builder``@SuperBuilder

@Jacksonized 
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class JacksonExample {
	private List<Foo> foos;
}

@StandardException

将此注释放在您自己的异常类型上(类新extends Exception或从继承的任何其他类Throwable。此注释将生成最多4个构造函数:

  • 一个无参数构造函数 ( MyException()),表示没有消息,也没有原因。
  • 仅消息构造函数 ( MyException(String message)),表示提供的消息,没有原因。
  • 仅原因构造函数 ( MyException(Throwable cause)),提供有从原因复制消息(如果),并使用的原因。
  • 完整的构造函数 ( MyException(String message, Throwable cause))。

每个构造函数都转发给完整的构造函数;您可以手动编写任何或所有这些构造函数,在这种情况下 lombok 不会生成它。完整的构造函数(如果需要生成)将调用super(message);,然后super.initCause(cause);在原因不为空时调用。

几乎没有理由将此注释放在所有自定义异常上。

import lombok.experimental.StandardException;

@StandardException
public class ExampleException extends Exception {
}

Vanilla Java


public class ExampleException extends Exception {
    public ExampleException() {
        this(null, null);
    }

    public ExampleException(String message) {
        this(message, null);
    }

    public ExampleException(Throwable cause) {
        this(cause != null ? cause.getMessage() : null, cause);
    }

    public ExampleException(String message, Throwable cause) {
        super(message);
        if (cause != null) super.initCause(cause);
    }
}