Lombok 简单使用
Lombok 简单讲解
参考
https://blog.csdn.net/ThinkWon/article/details/101392808
https://www.baeldung.com/intro-to-project-lombok
https://projectlombok.org/features/
介绍
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
添加依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
在 idea 中需要确保安装了 [Lombok 插件](https://so.csdn.net/so/search?q=Lombok 插件&spm=1001.2101.3001.7020)。
基本用法
生成 get 和 set 方法
使用 Lombok 的 @Getter 和 @Setter 方法,可以自动为我们生成 get 和 set 方法。
import lombok.Getter;
import lombok.Setter;
public class Person {
@Getter
@Setter
private String name;
private int age;
private String address;
}
编译后的代码:
public class Person {
private String name;
private int age;
private String address;
public Person() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
toString 方法
使用 Lombok 中的 @ToString 方法会自动为我们生成 toString 方法。
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ToString
public class Person {
@Getter
@Setter
private String name;
private int age;
private String address;
}
编译后的代码:
public class Person {
private String name;
private int age;
private String address;
public Person() {
}
public String toString() {
return "Person(name=" + this.getName() + ", age=" + this.age + ", address=" + this.address + ")";
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
equals 和 hashCode 方法
@EqualsAndHashCode 注解会同时生成 equals 方法和 hashCode 方法。
@EqualsAndHashCode
public class Address {
private String street;
private String city;
private String state;
private String postalCode;
}
编译后的代码:
public class Address {
private String street;
private String city;
private String state;
private String postalCode;
public Address() {
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Address)) {
return false;
} else {
Address other = (Address)o;
if (!other.canEqual(this)) {
return false;
} else {
label59: {
Object this$street = this.street;
Object other$street = other.street;
if (this$street == null) {
if (other$street == null) {
break label59;
}
} else if (this$street.equals(other$street)) {
break label59;
}
return false;
}
Object this$city = this.city;
Object other$city = other.city;
if (this$city == null) {
if (other$city != null) {
return false;
}
} else if (!this$city.equals(other$city)) {
return false;
}
Object this$state = this.state;
Object other$state = other.state;
if (this$state == null) {
if (other$state != null) {
return false;
}
} else if (!this$state.equals(other$state)) {
return false;
}
Object this$postalCode = this.postalCode;
Object other$postalCode = other.postalCode;
if (this$postalCode == null) {
if (other$postalCode != null) {
return false;
}
} else if (!this$postalCode.equals(other$postalCode)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Address;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $street = this.street;
result = result * 59 + ($street == null ? 43 : $street.hashCode());
Object $city = this.city;
result = result * 59 + ($city == null ? 43 : $city.hashCode());
Object $state = this.state;
result = result * 59 + ($state == null ? 43 : $state.hashCode());
Object $postalCode = this.postalCode;
result = result * 59 + ($postalCode == null ? 43 : $postalCode.hashCode());
return result;
}
}
对象的 hashCode 和 equals 方法特点
对于 hashCode 和 equals 方法,我们有以下三个结论:
结论 1:hashcode 相等,equals 不一定相等。
结论 2:equals 相等,hashcode 一定相等。
结论 3:hashcode 不相等,equals 一定不相等。
为什么需要 hashCode 和 equals 方法?
hashCode() 方法和 equal() 方法的作用其实一样,在 Java 里都是用来对比两个对象是否相等一致,一般来说 equals() 相等,那么我们可以直接说对比的两个对象是完全相等的了。那么既然 equal() 已经能实现对比的功能了,为什么还要 hashCode() 呢?
因为重写的 equal() 里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode() 进行对比,则只要生成一个 hash 值进行比较就可以了,效率很高,那么hashCode() 既然效率这么高为什么还要 equal() 呢?
这是由于 hashCode() 并不是完全可靠的,有时候不同的对象他们生成的 hashcode 也会一样(生成 hashcode 值的公式可能存在的问题),这也对应了上面提到的结论 1。所以 hashCode() 只能说是大部分时候可靠,并不是绝对可靠。
因此,比较好的方法是:每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
这种大量的并且快速的对象对比一般使用的 hash 容器中,比如 hashset, hashmap, hashtable 等等。比如 hashset 里要求对象不能重复,则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先 hashCode(),如果 hashCode()相同,再用 equals() 验证,如果 hashCode() 都不同,则肯定不同,这样对比的效率就很高了。
构造方法
@NoArgsConstructor:生成无参构造方法。
@AllArgsConstructor:生成所有字段的构造方法。
@RequiredArgsConstructor:当你在一个类中使用 @RequiredArgsConstructor 注解时,Lombok 会自动帮你生成一个构造函数,该构造函数仅包含类中定义的 final 字段或者被 @NonNull 注解标记的字段。
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private int id;
private String name;
private String department;
private double salary;
}
编译后的 java 代码:
public class Employee {
private int id;
private String name;
private String department;
private double salary;
public Employee(int id, String name, String department, double salary) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
}
public Employee() {
}
}
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.List;
@RequiredArgsConstructor
public class Order {
private final int orderId;
private List<String> products;
@NonNull
private double totalPrice;
}
编译后的代码:
import com.jxd.bug.Product;
import java.util.List;
import lombok.NonNull;
public class Order {
private final int orderId;
private List<Product> products;
private @NonNull double totalPrice;
public Order(int orderId, @NonNull double totalPrice) {
this.orderId = orderId;
this.totalPrice = totalPrice;
}
}
@RequiredArgsConstructor技巧
@RequiredArgsConstructor是Lombok中的一个注解,类似于@Autowired注解,主要的功能就是可以减少@Autowired的书写,我们在写controller或者Service层的时候,需要注入很多的mapper接口或者另外的service接口,这时候就会写很多的@Autowired注解,代码看起来很繁琐。
总结:类上加上@RequiredArgsConstructor,需要注入的类要用final声明。
@Slf4j
@Component
@RequiredArgsConstructor
public class GeneralMessageDemoProduce {
private final RocketMQTemplate rocketMQTemplate;
/**
* 发送普通消息
*
* @param topic 消息发送主题,用于标识同一类业务逻辑的消息
* @param tag 消息的过滤标签,消费者可通过Tag对消息进行过滤,仅接收指定标签的消息。
* @param keys 消息索引键,可根据关键字精确查找某条消息
* @param messageSendEvent 普通消息发送事件,自定义对象,最终都会序列化为字符串
* @return 消息发送 RocketMQ 返回结果
*/
public SendResult sendMessage(String topic, String tag, String keys, GeneralMessageEvent messageSendEvent) {
SendResult sendResult;
try {
StringBuilder destinationBuilder = StrUtil.builder().append(topic);
if (StrUtil.isNotBlank(tag)) {
destinationBuilder.append(":").append(tag);
}
Message<?> message = MessageBuilder
.withPayload(messageSendEvent)
.setHeader(MessageConst.PROPERTY_KEYS, keys)
.setHeader(MessageConst.PROPERTY_TAGS, tag)
.build();
sendResult = rocketMQTemplate.syncSend(
destinationBuilder.toString(),
message,
2000L
);
log.info("[普通消息] 消息发送结果:{},消息ID:{},消息Keys:{}", sendResult.getSendStatus(), sendResult.getMsgId(), keys);
} catch (Throwable ex) {
log.error("[普通消息] 消息发送失败,消息体:{}", JSON.toJSONString(messageSendEvent), ex);
throw ex;
}
return sendResult;
}
}
参考:https://blog.csdn.net/2301_79969868/article/details/144646459
高级用法
日志
lombok 提供了 @Slf4j、@Log、@CommonsLog、@Log4j、@Log4j2、@XSlf4j这些日志注解来对应不同的日志框架。
import lombok.extern.slf4j.Slf4j;
/**
* @author jxd
* {@code @date} 2024/3/26 21:47
*/
@Slf4j
public class Log {
public static void main(String[] args) {
log.info("@Slf4j annotation");
}
}
编译后的代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log {
private static final Logger log = LoggerFactory.getLogger(Log.class);
public Log() {
}
public static void main(String[] args) {
log.info("@Slf4j annotation");
}
}
@Data 注解
相当于这些注解的一个聚合:
- @ToString
- @EqualsAndHashCode
- @Getter
- @Setter
- @RequiredArgsConstructor
@Builder 注解
@Builder 注解会自动为我们生成 bean 对应的 Builder,有了这个 Builder,就可以使用通过链式调用的方式来构建对象(建造者模式)。
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class Customer {
private String customerId;
private String name;
private String email;
private String phoneNumber;
}
编译后的代码:
public class Customer {
private String customerId;
private String name;
private String email;
private String phoneNumber;
Customer(String customerId, String name, String email, String phoneNumber) {
this.customerId = customerId;
this.name = name;
this.email = email;
this.phoneNumber = phoneNumber;
}
public static CustomerBuilder builder() {
return new CustomerBuilder();
}
public String toString() {
return "Customer(customerId=" + this.customerId + ", name=" + this.name + ", email=" + this.email + ", phoneNumber=" + this.phoneNumber + ")";
}
public static class CustomerBuilder {
private String customerId;
private String name;
private String email;
private String phoneNumber;
CustomerBuilder() {
}
public CustomerBuilder customerId(String customerId) {
this.customerId = customerId;
return this;
}
public CustomerBuilder name(String name) {
this.name = name;
return this;
}
public CustomerBuilder email(String email) {
this.email = email;
return this;
}
public CustomerBuilder phoneNumber(String phoneNumberCu
......
使用 Builder 去构造 Customer 对象:
import org.junit.jupiter.api.Test;
/**
* @author jxd
* {@code @date} 2024/3/26 22:05
*/
public class UseCustomer {
@Test
public void useBuilder() {
final Customer tom = new Customer.CustomerBuilder()
.email("example.163.com")
.customerId("1")
.phoneNumber("1121")
.name("tom")
.build();
System.out.println(tom);
}
}
懒加载
@Getter(lazy = true) 它会在第一次调用这个getter时计算一次值,然后从那里开始缓存它。如果计算该值需要大量CPU,或者该值占用大量内存,这可能很有用。
生成懒加载的 getter 方法。注解加在一个被private final修饰的属性上,并且为其准备一个初始化方法。
public class LazyGetterExample {
@Getter(lazy = true)
private final int expense = calculateExpense();
private int calculateExpense() {
System.out.println("初次使用,计算开销!");
return 100;
}
public static void main(String[] args) {
LazyGetterExample example = new LazyGetterExample();
// 第一次调用 getter 方法,会计算并缓存值
System.out.println("开销为:" + example.getExpense());
// 第二次调用 getter 方法,直接返回缓存的值,不会重新计算
System.out.println("开销为:" + example.getExpense());
// 第三次调用 getter 方法,直接返回缓存的值,不会重新计算
System.out.println("开销为:" + example.getExpense());
// 第四次调用 getter 方法,直接返回缓存的值,不会重新计算
System.out.println("开销为:" + example.getExpense());
}
}
常见坑
boolean 类型的字段
@Data
public class Product {
private String name;
private double price;
private String description;
/**
* boolean 类型的字段会生成 is 方法,而不会生成 get 方法
*/
private boolean sellOut;
}
public class Product {
private String name;
private double price;
private String description;
private boolean sellOut;
public Product() {
}
public String getName() {
return this.name;
}
public double getPrice() {
return this.price;
}
public String getDescription() {
return this.description;
}
public boolean isSellOut() {
return this.sellOut;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(double price) {
this.price = price;
}
public void setDescription(String description) {
this.description = description;
}
public void setSellOut(boolean sellOut) {
this.sellOut = sellOut;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Product)) {
return false;
} else {
Product other = (Product)o;
if (!other.canEqual(this)) {
return false;
} else if (Double.compare(this.getPrice(), other.getPrice()) != 0) {
return false;
} else if (this.isSellOut() != other.isSellOut()) {
return false;
} else {
label40: {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name == null) {
break label40;
}
} else if (this$name.equals(other$name)) {
break label40;
}
return false;
}
Object this$description = this.getDescription();
Object other$description = other.getDescription();
if (this$description == null) {
if (other$description != null) {
return false;
}
} else if (!this$description.equals(other$description)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Product;
}
public int hashCode() {
int PRIME = true;
int result = 1;
long $price = Double.doubleToLongBits(this.getPrice());
result = result * 59 + (int)($price >>> 32 ^ $price);
result = result * 59 + (this.isSellOut() ? 79 : 97);
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $description = this.getDescription();
result = result * 59 + ($description == null ? 43 : $description.hashCode());
return result;
}
public String toString() {
return "Product(name=" + this.getName() + ", price=" + this.getPrice() + ", description=" + this.getDescription() + ", sellOut=" + this.isSellOut() + ")";
}
sellOut 字段是 Boolean 类型,没有生成它的 get 方法,生成了一个 isSellOut 方法。
名称不规则字段的 get set 方法名问题
import lombok.Data;
@Data
public class Book {
private String isbn;
private String title;
private String aAuthor;
private int pageCount;
private double price;
}
编译后的代码:
public class Book {
private String isbn;
private String title;
private String aAuthor;
private int pageCount;
private double price;
// 省略无关代码
public String getAAuthor() {
return this.aAuthor;
}
public void setAAuthor(String aAuthor) {
this.aAuthor = aAuthor;
}
}
使用 idea 生成的 get 和 set 方法却是:
public String getaAuthor() {
return aAuthor;
}
public void setaAuthor(String aAuthor) {
this.aAuthor = aAuthor;
}
因为这个原因,导致 lombok 和 mybatis 一起使用时有时就会产生问题。
生成的 equals 方法
使用 @Data 注解生成的 equals 方法,默认只判断了当前类的属性是否相等,而忽略了父类的属性,从而使两个明显不相等的对象通过 equals 判断为相等。
解决方式就是使用 @EnqualsAndHashCode 注解并显示指定 canSuper=true,这样就会调用父类的 equals 方法。
import org.junit.jupiter.api.Test;
/**
* @author jxd
* {@code @date} 2024/3/26 22:35
*/
public class TestCode {
@Test
public void equals() {
final PictureBook pictureBook1 = new PictureBook();
pictureBook1.setIsbn("1");
pictureBook1.setPictureNm(21);
final PictureBook pictureBook2 = new PictureBook();
pictureBook2.setIsbn("2");
pictureBook2.setPictureNm(21);
System.out.println(pictureBook1.equals(pictureBook2));
}
}
import lombok.Data;
@Data
public class Book {
private String isbn;
private String title;
private String aAuthor;
private int pageCount;
private double price;
}
import lombok.Data;
/**
* @author jxd
* {@code @date} 2024/3/26 22:34
*/
@Data
public class PictureBook extends Book {
private Integer pictureNm;
}
上面定义了一个 PictureBook 类,使用了 @Data 注解,如果调用 PictureBook 的 equals 方法,默认只会根据 pictureNm 来判断两个 PictureBook 对象是否相等。
需要添加如下的注解,才会调用父类的 equals 方法。
本文作者:王陸
本文链接:https://www.cnblogs.com/wkfvawl/p/18710002
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-02-11 Elasticsearch(二)进阶、优化、面试题
2022-02-11 SparkSQL