Lombok是一个Java的库,日常开发使用过程使用的很频繁,个人使用也只是加@Data和两个构造器的注解,所以抽时间研究了下其他的注解

@Getter

@Getter注解是在成员变量和类上,用来生成对应的get方法,其中有三个属性,一个是lazy,另一个是onMethod属性,还有一个是value属性

boolean lazy() default false;

lazy属性是对final字段进行使用,在属性初始化时,使用懒加载来进行初始化

AnyAnnotation[] onMethod() default {};

onMethod属性,则标注该字段在生成get方法时,同时在方法上加入一个设置好的注解(注:该属性为实验属性)
Demo如下

public class GetterTest {
  @Getter(lazy = true)
  private final String field1 = "zhangsan";

  @Getter(
      value = AccessLevel.PRIVATE,
      onMethod_ = {@NonNull})
  private String field2;
}

编译后生成的代码如下

public class GetterTest {
  private final AtomicReference<Object> field1 = new AtomicReference();
  private String field2;

  public GetterTest() {
  }

  public String getField1() {
    Object value = this.field1.get();
    if (value == null) {
      synchronized(this.field1) {
        value = this.field1.get();
        if (value == null) {
          String actualValue = "zhangsan";
          value = "zhangsan" == null ? this.field1 : "zhangsan";
          this.field1.set(value);
        }
      }
    }

    return (String)(value == this.field1 ? null : value);
  }

  private @NonNull String getField2() {
    return this.field2;
  }
}

这里onMethod在IDE中会报错,但编译并不会报错,
为什么会这么写,官方文档有如下解释
image
value属性是用来指定生成的get方式的访问权限,private protected public之类的

@Setter注解

@Setter注解同上@Getter注解一致,是生成对应字段的set方法,其中属性也有三个,与@Getter相同的两个value和onMethod方法,还有一个onParam
onParam与onMethod使用起来类似,只不过一个是注解加在方法上,另一个是加
在参数列表上(注:这也是一个实验功能)
Demo如下

public class SetterTest {
  private String field1;
  private String field2;

  public SetterTest() {
  }

  public void setField1(final String field1) {
    this.field1 = field1;
  }

  @NonNull
  private void setField2(final @NonNull String field2) {
    if (field2 == null) {
      throw new NullPointerException("field2 is marked non-null but is null");
    } else {
      this.field2 = field2;
    }
  }
}

@ToString注解

顾名思义,这是一个用来生成类toString方法的注解,它里面有这么几个属性,callSuper,是否同时生成父类的toString方法到子类中,includeFieldNames,是否在生成的toString方法中使用对应的get方法拼接,exclude,排除掉某个属性,of与exclude有冲突,同时存在,优先级更高,指定某个属性要参与toString方法
Demo如下

@ToString(
    //    callSuper = true,
    includeFieldNames = false,
    //    exclude = {"field1"},与of有冲突
    //    of = {"field1"},
    doNotUseGetters = true
    //
    )
public class ToStringTest {
  @Setter private String field1;
  @Setter private String field2;

  public String getField2() {
    System.out.println("调用get方法");
    return field2;
  }
public class ToStringTest {
  private String field1;
  private String field2;

  public ToStringTest() {
  }

  public String getField2() {
    System.out.println("调用get方法");
    return this.field2;
  }

  @Test
  public void test() {
    ToStringTest toStringTest = new ToStringTest();
    toStringTest.setField1("zhangsan");
    toStringTest.setField2("lisi");
    System.out.println(toStringTest.toString());
  }

  public String toString() {
    return "ToStringTest(" + this.field1 + ", " + this.field2 + ")";
  }

  public void setField1(final String field1) {
    this.field1 = field1;
  }

  public void setField2(final String field2) {
    this.field2 = field2;
  }
}

@EqualsAndHashCode注解

这个也是顾名思义,是用来生成equals和HashCode方法的,它有的几个属性与上方@ToString是相同的
Demo如下

@EqualsAndHashCode(exclude = {"field1"})
public class EqualsAndHashCodeTest {
  private String field1;
  private String field2;
}

生成的代码

public class EqualsAndHashCodeTest {
  private String field1;
  private String field2;

  public EqualsAndHashCodeTest() {
  }

  public boolean equals(final Object o) {
    if (o == this) {
      return true;
    } else if (!(o instanceof EqualsAndHashCodeTest)) {
      return false;
    } else {
      EqualsAndHashCodeTest other = (EqualsAndHashCodeTest)o;
      if (!other.canEqual(this)) {
        return false;
      } else {
        Object this$field2 = this.field2;
        Object other$field2 = other.field2;
        if (this$field2 == null) {
          if (other$field2 != null) {
            return false;
          }
        } else if (!this$field2.equals(other$field2)) {
          return false;
        }

        return true;
      }
    }
  }

  protected boolean canEqual(final Object other) {
    return other instanceof EqualsAndHashCodeTest;
  }

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

上述生成的代码中还有一个canEqual方法,;用来比较对象是否是该类的一个实例

@Data注解

这个注解是一个复合注解,包含了@Getter,@Setter,@ToString,@EqualsAndHashCode,@RequiredArgsConstructor(这个注解我们后续再谈),这个注解对于我们常用的pojo类十分有用

@Val注解

是一个类型推断的注解,由于在JDK10中已加入类似的功能,这里不多描述

@NonNull注解

用于在代码中生成一个判空的代码
Demo如下

public class NonNullTest {

  public NonNullTest(@NonNull String field) {
    System.out.println(field);
  }
}

生成的代码如下

public class NonNullTest {
  public NonNullTest(@NonNull String field) {
    if (field == null) {
      throw new NullPointerException("field is marked non-null but is null");
    } else {
      System.out.println(field);
    }
  }
}

这个一般开发中使用Hibernate-validation所以并不经常使用

@RequiredArgsConstructor @AllArgsConstructor @NoArgsConstructor

@AllArgsConstructor和@NoArgsConstructor一个是用来生成全参构造函数,另一个是用来生成无参构造函数,有一个特殊的属性staticName,这个是用来生成一个静态的构造函数来进行构造对象,一般使用"of"(官方文档推荐)
@RequiredArgsConstructor则是用来生成那些final和@NonNull修饰的字段的构造函数,如果这个final的字段已经有值,则不会在构造器中,在@AllArgsConstructor也是不会生成的

Demo如下

@RequiredArgsConstructor
@AllArgsConstructor(staticName = "of")

public class ConstructorTest {
  private final String field1;
  @NonNull private String field2;
  private String field3;
  private final String field4 = "已完成初始化的字段";
}

生成的代码如下

public class ConstructorTest {
  private final String field1;
  private @NonNull String field2;
  private String field3;
  private final String field4 = "已完成初始化的字段";

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

  private ConstructorTest(final String field1, final @NonNull String field2, final String field3) {
    if (field2 == null) {
      throw new NullPointerException("field2 is marked non-null but is null");
    } else {
      this.field1 = field1;
      this.field2 = field2;
      this.field3 = field3;
    }
  }

  public static ConstructorTest of(final String field1, final @NonNull String field2, final String field3) {
    return new ConstructorTest(field1, field2, field3);
  }
}

@Cleanup注解

这个注解用来自动生成关闭资源的代码,现在一般使用try-with-resource,所以这个使用也很少
这里补充try-with-resource要注意的点
1.资源对象被return的情况下,由调用方关闭
2.ByteArrayInputStream等不需要检查关闭的资源对象
image-1651475969225
3.使用Socket获取的InputStream和OutputStream对象不需要关闭

@Builder注解

@Builder注解是用来生成建造者模式模板代码的注解,这是比较推荐使用的一个注解,但也有几个注意点
1.static 字段是不会生成相应的模板代码的
2.已经赋值的static final字段,是不会在build()生成的对象中
3.build()生成的对象是不可变的,这点可以在生成中的代码中体现
4.@Builder不能生成父类的建造者模式的模板,官网在实验特性中有@SuperBuilder注解
5.没有任何修饰符端字段如果有值,在编译时会报错
image-1651478499540
这里我们需要@Builder.Default来进行处理
image-1651478843177
6.如果手动添加无参构造器,自己new出的对象不会有默认值
@Builder可以与@Singular注解搭配使用,@Singular注解用于集合(注:需要指定集合端字段名,不然编译时会报错)
Demo如下:

@Builder
@Data
@AllArgsConstructor
public class BuilderTest {
  private static String staticField;
//  private final String finalField;
  private String field1;
  @Builder.Default private String field2 = "init";
  private static final String initFinalField = "已初始化的Final字段";

  @Singular("listField")
  private List<String> listField;

  public BuilderTest() {
  }

  public static void main(String[] args) {
    var build =
        BuilderTest.builder()
            .finalField("手动复制Final-field字段")
            .field1("手动赋值Field字段")
            .listField("张三")
            .listField("李四")
            //            .listField(new ArrayList<>())
            // 此时创建的对象是不可变得
            .build();
    System.out.println(JSON.toJSONString(build));
//    var builderTest = new BuilderTest();
//    System.out.println(JSON.toJSONString(builderTest));
  }
}

生成的代码如下

public class BuilderTest {
  private static String staticField;
  private final String finalField;
  private String field1;
  private String field2;
  private static final String initFinalField = "已初始化的Final字段";
  private List<String> listField;

  public static void main(String[] args) {
    BuilderTest build = builder().finalField("手动复制Final-field字段").field1("手动赋值Field字段").listField("张三").listField("李四").build();
    System.out.println(JSON.toJSONString(build));
  }

  private static String $default$field2() {
    return "init";
  }

  BuilderTest(final String finalField, final String field1, final String field2, final List<String> listField) {
    this.finalField = finalField;
    this.field1 = field1;
    this.field2 = field2;
    this.listField = listField;
  }

  public static BuilderTestBuilder builder() {
    return new BuilderTestBuilder();
  }

  public String getFinalField() {
    return this.finalField;
  }

  public String getField1() {
    return this.field1;
  }

  public String getField2() {
    return this.field2;
  }

  public List<String> getListField() {
    return this.listField;
  }

  public void setField1(final String field1) {
    this.field1 = field1;
  }

  public void setField2(final String field2) {
    this.field2 = field2;
  }

  public void setListField(final List<String> listField) {
    this.listField = listField;
  }

  public boolean equals(final Object o) {
    if (o == this) {
      return true;
    } else if (!(o instanceof BuilderTest)) {
      return false;
    } else {
      BuilderTest other = (BuilderTest)o;
      if (!other.canEqual(this)) {
        return false;
      } else {
        label59: {
          Object this$finalField = this.getFinalField();
          Object other$finalField = other.getFinalField();
          if (this$finalField == null) {
            if (other$finalField == null) {
              break label59;
            }
          } else if (this$finalField.equals(other$finalField)) {
            break label59;
          }

          return false;
        }

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

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

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

        return true;
      }
    }
  }

  protected boolean canEqual(final Object other) {
    return other instanceof BuilderTest;
  }

  public int hashCode() {
    int PRIME = true;
    int result = 1;
    Object $finalField = this.getFinalField();
    result = result * 59 + ($finalField == null ? 43 : $finalField.hashCode());
    Object $field1 = this.getField1();
    result = result * 59 + ($field1 == null ? 43 : $field1.hashCode());
    Object $field2 = this.getField2();
    result = result * 59 + ($field2 == null ? 43 : $field2.hashCode());
    Object $listField = this.getListField();
    result = result * 59 + ($listField == null ? 43 : $listField.hashCode());
    return result;
  }

  public String toString() {
    String var10000 = this.getFinalField();
    return "BuilderTest(finalField=" + var10000 + ", field1=" + this.getField1() + ", field2=" + this.getField2() + ", listField=" + this.getListField() + ")";
  }

  public static class BuilderTestBuilder {
    private String finalField;
    private String field1;
    private boolean field2$set;
    private String field2$value;
    private ArrayList<String> listField;

    BuilderTestBuilder() {
    }

    public BuilderTestBuilder finalField(final String finalField) {
      this.finalField = finalField;
      return this;
    }

    public BuilderTestBuilder field1(final String field1) {
      this.field1 = field1;
      return this;
    }

    public BuilderTestBuilder field2(final String field2) {
      this.field2$value = field2;
      this.field2$set = true;
      return this;
    }

    public BuilderTestBuilder listField(final String listField) {
      if (this.listField == null) {
        this.listField = new ArrayList();
      }

      this.listField.add(listField);
      return this;
    }

    public BuilderTestBuilder listField(final Collection<? extends String> listField) {
      if (listField == null) {
        throw new NullPointerException("listField cannot be null");
      } else {
        if (this.listField == null) {
          this.listField = new ArrayList();
        }

        this.listField.addAll(listField);
        return this;
      }
    }

    public BuilderTestBuilder clearListField() {
      if (this.listField != null) {
        this.listField.clear();
      }

      return this;
    }

    public BuilderTest build() {
      List listField;
      switch (this.listField == null ? 0 : this.listField.size()) {
        case 0:
          listField = Collections.emptyList();
          break;
        case 1:
          listField = Collections.singletonList((String)this.listField.get(0));
          break;
        default:
          listField = Collections.unmodifiableList(new ArrayList(this.listField));
      }

      String field2$value = this.field2$value;
      if (!this.field2$set) {
        field2$value = BuilderTest.$default$field2();
      }

      return new BuilderTest(this.finalField, this.field1, field2$value, listField);
    }

    public String toString() {
      return "BuilderTest.BuilderTestBuilder(finalField=" + this.finalField + ", field1=" + this.field1 + ", field2$value=" + this.field2$value + ", listField=" + this.listField + ")";
    }
  }
}

上述代码可以看出@Builder在类中构建一个类,在构建过程中与外界隔绝
@Singular生成了一个可以逐一添加属性到集合中的方法,同时也生成了一个clear的方法,用于清除集合中元素
同时再写一个Builde.Default的Demo

@Builder
@Data
public class BuilderDefaultTest {

  @Builder.Default private final String id = String.valueOf(new Random().nextInt());

  private String username;

  @Builder.Default private long insertTime = System.currentTimeMillis();

  public static void main(String[] args) {
    var build = BuilderDefaultTest.builder().build();
    System.out.println(JSON.toJSONString(build));

  }
}

@Value注解

这个注解与@Data类似,类中所有字段都会加上final字段,且不会生成setter方法,适合于只读类,使用场景较少

@SneakyThrows注解

使用在抛出受检异常的方法上,自动生成throws代码,有看到idea提示过,但一般还是自己抛出,使用场景较少

@Synchronized注解

用于方法上,将方法自动生成时变成上锁 同步的,锁为类中一个私有对象
Demo如下

public class SynchronizedTest {
  @Synchronized
  public void sycn() {
    System.out.println("Hello World");
  }
}

生成的代码如下

public class SynchronizedTest {
  private final Object $lock = new Object[0];

  public SynchronizedTest() {
  }

  public void sycn() {
    synchronized(this.$lock) {
      System.out.println("Hello World");
    }
  }
}

val关键字

Kotiln也有此关键字,二者意思相同,都是在这个字段上声明final

@Accessors注解

这个注解在别人的代码中看到了一次
这个注解有四个属性,有一个是近期出的,这里按下不表
fluent,chain,prefix
fluent 生成的getter方法是字段名(),生成的setter方法则返回对象本身

@Getter
@Setter
@Accessors(fluent = true)
//这里会隐式的将chain也设置为true
public class AccessorsTest {
  private String field1;
  private String field2;

  public static void main(String[] args) {
    var accessorsTest = new AccessorsTest();
    accessorsTest.field1("field1").field2("field2");
  }
}

生成的代码如下

public class AccessorsTest {
  private String field1;
  private String field2;

  public AccessorsTest() {
  }

  public static void main(String[] args) {
    AccessorsTest accessorsTest = new AccessorsTest();
    accessorsTest.field1("field1").field2("field2");
  }

  public String field1() {
    return this.field1;
  }

  public String field2() {
    return this.field2;
  }

  public AccessorsTest field1(final String field1) {
    this.field1 = field1;
    return this;
  }

  public AccessorsTest field2(final String field2) {
    this.field2 = field2;
    return this;
  }
}

chain则是将setter方法返回值设置为对象本身,方便链式调用
如同上方实例
prefix则是去掉属性前缀
如果一个字段是pName,这时设置prefix为"p",那么生成的getter和Setter方法就会忽略掉这个前缀

Q.E.D.