这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战
介绍
Lombok减少了编写一些初始化方法、get/set方法、异常处理等提供了很大的便利。
Builder
初始化函数
@Builder注解本意是提供链式写法的建造器,但是若仅仅在实体上添加这个注解由于建造器在最后build()方法时要调用实体的全参构造函数,所以@Builder也生成了对应的全参构造函数,这就导致这个实体没有了无参构造函数。所以如果还是有需要通过new方式创建对象,需要手动添加一个无参构造函数或者使用@NoArgsConstructor注解自动生成,本以为添加了无参构造函数的注解就可以了,但是添加之后发现,好像这个无参构造函数的注解会覆盖@Builder生成的全参构造函数,导致添加之后编译报错。所以如果即想使用链式结构也想使用new方式,那么需要至少需要标记三个注解@Builder、@NoArgsConstructor、@AllArgsConstructor
@Builder
public class Doctor {
private String id;
private String name;
}
// 反编译
public class Doctor {
private String id;
private String name;
Doctor(final String id, final String name) {
this.id = id;
this.name = name;
}
public static Doctor.DoctorBuilder builder() {
return new Doctor.DoctorBuilder();
}
public static class DoctorBuilder {
private String id;
private String name;
DoctorBuilder() {
}
public Doctor.DoctorBuilder id(final String id) {
this.id = id;
return this;
}
public Doctor.DoctorBuilder name(final String name) {
this.name = name;
return this;
}
public Doctor build() {
return new Doctor(this.id, this.name);
}
public String toString() {
}
}
}
继承类
如果在父类添加了@Builder子类中就不能添加@Builder注解,这是由于子类的建造器无法访问到父类中的建造器,所以若实体之间存在继承关系之间只能有一者标记@Builder,如果是子类标记@Builder那么这时子类的建造器也是无法获取到父类的属性。所以基本上@Builder标记在父类还是子类同时添加其他注解进行辅助,也无法满足所有的场景。
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Parent {
private String parentId;
private String parentName;
}
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Son extends Parent{
private String sonId;
private String sonName;
}
当然了在高版本的lombok,可以使用@SuperBuilder,在父类和子类上都标记@SuperBuilder注解,就可以进行子类通过builder方法链式调用父类属性。且使用@SuperBuilder注解标记父子实体后,就无需使用全参构造函数和无参构造函数注解。
SneakyThrows
在java的异常体系中Exception异常有两个分支,一个是运行时异常RuntimeException,一个是编译时异常,在Exception下的所有非RuntimeException异常,比如IOException、SQLException等;所有的运行时异常不捕获,编译时异常是一定要捕获,否则编译会报错。@SneakyThrows就是利用了这一机制,将当前方法抛出的异常,包装成RuntimeException,骗过编译器,使得调用点可以不用显示处理异常信息。
若不使用@SneakyThrows注解,newInsstance方法会要求抛出InstantiationException,
IllegalAccessException异常,且调用sneakyThrowsTest方法的地方需要捕获这些异常,
加上@SneakyThrows注解之后就不需要捕获异常信息。
@SneakyThrows
private void sneakyThrowsTest(){
SneakyThrowsDemo.class.newInstance();
}
以下为反编译后的代码,调用Lombok的方法被转化为RuntimeException允许时异常。
private void sneakyThrowsTest() {
try {
HelloController.class.newInstance();
} catch (Throwable e) {
throw Lombok.sneakyThrow(e);
}
}
public static RuntimeException sneakyThrow(Throwable t) {
if (t == null) {
throw new NullPointerException("t");
} else {
return Lombok.<RuntimeException>sneakyThrow0(t);
}
}
这个方法是关键,这里对入参类型的约束为,而调用点将异常强转为 运行时异常。
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T)t;
}
那么问题来了,为什么这个地方可以对原来的异常进行强转为RuntimeExcption?以下为直接强转的代码,显然运行之后报类型转换异常。直接将e强转为运行时异常,运行到这里会报类型转换异常。
private void sneakyThrowsTest() {
try {
throw new Exception();
} catch (Throwable e) {
throw (RuntimeException)e;
}
}
实际上,这种做法是一种通过泛型欺骗了编译器,让编译器在编译期不报错,而最后在JVM虚拟机中执行的字节码的并没有区别编译时异常和运行时异常,只有是不是和抛不抛异常而已。