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中会报错,但编译并不会报错,
为什么会这么写,官方文档有如下解释
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等不需要检查关闭的资源对象
3.使用Socket获取的InputStream和OutputStream对象不需要关闭
@Builder注解
@Builder注解是用来生成建造者模式模板代码的注解,这是比较推荐使用的一个注解,但也有几个注意点
1.static 字段是不会生成相应的模板代码的
2.已经赋值的static final字段,是不会在build()生成的对象中
3.build()生成的对象是不可变的,这点可以在生成中的代码中体现
4.@Builder不能生成父类的建造者模式的模板,官网在实验特性中有@SuperBuilder注解
5.没有任何修饰符端字段如果有值,在编译时会报错
这里我们需要@Builder.Default来进行处理
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.