Java中lombok的使用详解
文章目录
在开发过程中,有一些代码总是要重复编写,比如 getter / setter、构造器方法、toString方法等等,很浪费时间,为了节约时间使用 lombok 插件来减少相同代码的编写,使用注解来实现,本文将解释一下 lombok 中的常用注解的一些技巧。
lombok的安装
- 首先在 IDEA 中安装 lombok 插件,一般情况下该插件已经和 IDE 绑定安装了。
- 添加 maven 依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
完成上述两个步骤就可以在代码中使用 lombok 了。
lombok的使用
@Data
在类上注解,提供该类所有属性的 getter/setter 方法,还提供了 equals、canEqual、hashCode、toString 方法。
使用 @Data 注解如下:
@Data
public class Person {
private String name;
private int age;
}
经过编译代码如下:
public class Person {
private String name;
private int age;
public Person() {
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public void setName(final String name) {
this.name = name;
}
public void setAge(final int age) {
this.age = age;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Person)) {
return false;
} else {
Person other = (Person)o;
if (!other.canEqual(this)) {
return false;
} else if (this.getAge() != other.getAge()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof Person;
}
public int hashCode() {
int PRIME = true;
int result = 1;
result = result * 59 + this.getAge();
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "Person(name=" + this.getName() + ", age=" + this.getAge() + ")";
}
}
@Getter、@Setter
在属性上注解:为该属性提供getter方法
public class Person {
@Getter
private String name;
private int age;
}
在类上注解:为该类所有属性提供getter方法
@Getter
public class Person {
private String name;
private int age;
}
@Setter注解和@Getter是一个意思,不在重复粘贴代码展示。
@Slf4j
作用在类上,此注解一般在业务层使用,将日志打印出来。
代码如下:(下面代码只是为了举例,业务不是重点)
@Service
@Slf4j
public class TbCustomerServiceImpl extends ServiceImpl<TbCustomerMapper, TbCustomer> implements TbCustomerService {
private StudentService studentService;
public TbCustomerServiceImpl(StudentService studentService) {
this.studentService = studentService;
}
@Override
public List<TbCustomer> queryAll() {
List<TbCustomer> list = this.lambdaQuery().list();
// 花括号为占位符,将list的数据放在其中
log.debug("查出的数据为 {}", list);
return list;
}
}
@AllArgsConstructor
作用在类上,使该类提供一个全参构造方法,此时该类只有全参构造方法,没有默认的无参构造方法。
代码如下:
@AllArgsConstructor
public class Person {
private String name;
private int age;
}
编译后代码如下:
public class Person {
private String name;
private int age;
public Person(final String name, final int age) {
this.name = name;
this.age = age;
}
}
@NoArgsConstructor
作用在类上,使该类提供一个无参构造方法,一般配合@AllArgsConstructor此注解一起使用,同时提供全参构造方法和无参构造方法。
还有一种我比较喜欢的用法:
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor(staticName = "of")
public class Person {
private String name;
private int age;
}
加了staticName这个参数可以更快的生成对象:
public void Test(){
Person lisi = Person.of("lisi", 2);
Person of = Person.of();
}
当只有@Data注解时,加了staticConstructor参数如下:
@Data(staticConstructor = "of")
public class Person {
private String name;
private int age;
}
则默认使用无参构造创建对象:
public void Test(){
Person of = Person.of();
}
@EqualsAndHashCode
此注解作用于类上,生成equals、canEqual、hashCode方法。
同时还有一个优点,在重写equals方法时同时重写了HashCode方法,为啥要都重写才行呢,我下面举个例子来说明:
- 我们有个只重写equals方法的类,使用set集合来存储一些这个类对象(这些类对象要有重复的)
- 当循环打印这个set集合应该把重复的对象去掉,但是并没有
代码如下:
@Test
public void Test(){
HashSet<Person> set = new HashSet<>();
set.add(new Person("lisi",22));
set.add(new Person("lisi",22));
set.stream().forEach(s -> System.out.println(s));
}
打印的日志如下:
Person(name=lisi, age=22)
Person(name=lisi, age=22)
原因如下:
- 这是因为去重时先去比较了hashcode是否相同,因为没有重写hashcode方法所以去执行了object类中的hashcode方法,返回的是不同引用地址的对象所以是false,直接不执行equals方法,所以最后返回的也是false。
- 如果重写了hashcode方法,进行比较时对比的是对象所有属性的hashcode值是否相同,返回结果自然是true,在去调用equals方法,发现两个方法确实是相等的,于是返回true,程序才能执行正常。
如果没有听懂建议去搜索对应问题的详细回答,我这里只是进行简述。
@NonNull
作用于属性上,提供关于此参数的非空检查,如果参数为空,则抛出空指针异常。
public class Person {
@NonNull
private String name;
private int age;
}
@RequiredArgsConstructor
该注解作用于类上,所有带有@NonNull注解或者带有final修饰的成员变量作为参数生成构造方法。(构造注入)
代码如下:
@Service
@RequiredArgsConstructor
public class TbCustomerServiceImpl extends ServiceImpl<TbCustomerMapper, TbCustomer> implements TbCustomerService {
private final StudentService studentService;
@Override
public Student selectStudentById() {
Student student = studentService.selectStudentById(2);
return student;
}
}
@ToString
作用在类上,为所有属性提供toString方法,具体代码见上方@Data注解。
@SneakyThrows
作用于方法上,将方法内的代码添加了一个try-catch处理,捕获异常catch中用Lombok.sneakyThrow(e)抛出异常。使用@SneakyThrows(BizException.class)指定抛出具体异常。
代码如下:
@SneakyThrows
public void Test() {
int num = 0;
num = 1 / 0;
}
编译后如下:
public void Test() {
try {
int num = 0;
num = 1 / 0;
} catch (Throwable var3) {
throw var3;
}
}
@Builder
作用于类上,实现流式操作,思想为设计模式中的创建者模式。
代码如下:
@Builder
public class Person {
private String name;
private int age;
}
如何使用:
public void Test(){
Person lisi = Person.builder().age(11).name("lisi").build();
}
上述流式操作可以不全传参数,不穿的参数为null。
@Cleanup
作用于变量,保证该变量代表的资源会被自动关闭,默认调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定。
代码举例如下:
public class FileCopyExample {
public static void main(String[] args) {
String sourceFile = "source.txt";
String targetFile = "target.txt";
try (
@Cleanup FileInputStream inputStream = new FileInputStream(sourceFile);
@Cleanup FileOutputStream outputStream = new FileOutputStream(targetFile);
) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Synchronized
作用于类方法或实例方法上,效果与synchronized相同。区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized的锁对象分别是私有静态final对象lock和私有final对象lock。也可以指定锁对象。
代码如下:
public class example{
private final Object lock = new Object();
@Synchronized
public void one(){
System.out.println("one1");
}
@Synchronized("lock")
public void two(){
System.out.println("two");
}
}
编译后如下:
public class example {
private static final Object $LOCK = new Object[0];
private final Object lock = new Object();
example() {
}
public static void one() {
synchronized($LOCK) {
System.out.println("one1");
}
}
public void two() {
synchronized(this.lock) {
System.out.println("two");
}
}
}
总结: Lombok可以提高代码的生产力和可读性,但也需要谨慎使用,有时也会无法满足需求也会让不熟悉的开发人员增加挑战等等、特别是在大型团队或有特殊要求的项目中。团队需要权衡Lombok的好处和坏处,并根据项目的具体需求决定是否使用它。