为什么要用lombo
- 个别javaBean的属性很多(多的有100多个,生成Setter/Getter太多,并且阅读代码体验很不好)
- 有的大佬在getter/setter里写业务逻辑或者数据处理导致错误的认为是属性本身的值错误(我并不认为在setter/getter里面添加业务逻辑有什么好的)
- 生成getter/setter的代码格式不统一(有的setter可能会返回this,有的是void,有的使用isXXX,有的使用getXXX)
- 增加属性的同时需要重新生成toString、equals等方法(假如有的话)
使用lombok带来好处:
- 简化JavaBean定义
- 提高开发效率
- 标准化、规范化JavaBean定义
引入lombok
lombok本身只需要引入jar就可以使用,但是为了编程的舒适性(编译不报错,能自动补全等)
还是需要IDE插件支持,以IDEA为例子(别的IDE可以看看插件github上有没有,详细地址见本文末尾提供的链接):
跟普通的IDEA插件一样,直接搜索lombok然后直接安装就可以,可能会提示需要jar引入也可能不会,然后重启IDEA,插件就装好了装好就跟跟下图一样。
引入依赖:
maven:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
jar:
下载页
怎么使用lombok
直接使用注解就可以使用lombok。
- @Getter/@Setter
很明显这两个注解的作用是生成Setter/getter,直接打注解在属性上就可以。
package demo;
import lombok.Getter;
import lombok.Setter;
public class UserDemo {
/**
* 姓名
*/
@Setter
private String name;
/**
* 年龄
*/
@Getter
private Integer age;
/**
* 联系电话
*/
@Setter
@Getter
private String mobile;
}
对应生成的class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
public class UserDemo {
private String name;
private Integer age;
private String mobile;
public UserDemo() {
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getMobile() {
return this.mobile;
}
}
- @ToString
生成toString
方法,其中注解可以设置·exclude·的值来设置哪些属性不需要被打印出来,callSuper
则可以控制是不是调用父类的toString
方法。
package demo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ToString
public class UserDemo {
/**
* 姓名
*/
@Setter
private String name;
/**
* 年龄
*/
@Getter
private Integer age;
/**
* 联系电话
*/
@Setter
@Getter
private String mobile;
}
对应生成的class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
public class UserDemo {
private String name;
private Integer age;
private String mobile;
public UserDemo() {
}
public String toString() {
return "UserDemo(name=" + this.name + ", age=" + this.getAge() + ", mobile=" + this.getMobile() + ")";
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getMobile() {
return this.mobile;
}
}
- @EqualsAndHashCode
直接根据属性生成equals
和hashCode
方法,这个方法有一个地方需要注意的是,假如类是一个子类,单纯的增加@EqualsAndHashCode
注解,是不会调用父类的方法的,这个时候需要增加callSuper=true
,完整的注解应该是@EqualsAndHashCode(callSuper = true)
package demo;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ToString
@EqualsAndHashCode
public class UserDemo {
/**
* 姓名
*/
@Setter
private String name;
/**
* 年龄
*/
@Getter
private Integer age;
/**
* 联系电话
*/
@Setter
@Getter
private String mobile;
}
对应生成的class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
public class UserDemo {
private String name;
private Integer age;
private String mobile;
public UserDemo() {
}
public String toString() {
return "UserDemo(name=" + this.name + ", age=" + this.getAge() + ", mobile=" + this.getMobile() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof UserDemo)) {
return false;
} else {
UserDemo other = (UserDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$name = this.name;
Object other$name = other.name;
if (this$name == null) {
if (other$name == null) {
break label47;
}
} else if (this$name.equals(other$name)) {
break label47;
}
return false;
}
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
Object this$mobile = this.getMobile();
Object other$mobile = other.getMobile();
if (this$mobile == null) {
if (other$mobile != null) {
return false;
}
} else if (!this$mobile.equals(other$mobile)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof UserDemo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.name;
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $mobile = this.getMobile();
result = result * 59 + ($mobile == null ? 43 : $mobile.hashCode());
return result;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getMobile() {
return this.mobile;
}
}
- @NoArgsConstructor/@AllArgsConstructor
这两个注解都是用于生成构造函数的,一个是没有参数的,一个是所有参数的。
注解本身还有个可选额属性access
,可以设置方法的访问修饰符(public
、private
等)
package demo;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class UserDemo {
/**
* 姓名
*/
@Setter
private String name;
/**
* 年龄
*/
@Getter
private Integer age;
/**
* 联系电话
*/
@Setter
@Getter
private String mobile;
}
对应生成的calsss
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
public class UserDemo {
private String name;
private Integer age;
private String mobile;
public String toString() {
return "UserDemo(name=" + this.name + ", age=" + this.getAge() + ", mobile=" + this.getMobile() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof UserDemo)) {
return false;
} else {
UserDemo other = (UserDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$name = this.name;
Object other$name = other.name;
if (this$name == null) {
if (other$name == null) {
break label47;
}
} else if (this$name.equals(other$name)) {
break label47;
}
return false;
}
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
Object this$mobile = this.getMobile();
Object other$mobile = other.getMobile();
if (this$mobile == null) {
if (other$mobile != null) {
return false;
}
} else if (!this$mobile.equals(other$mobile)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof UserDemo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.name;
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $mobile = this.getMobile();
result = result * 59 + ($mobile == null ? 43 : $mobile.hashCode());
return result;
}
public UserDemo() {
}
public UserDemo(String name, Integer age, String mobile) {
this.name = name;
this.age = age;
this.mobile = mobile;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getMobile() {
return this.mobile;
}
}
- @NonNull
切记不是@NotNull,@NotNull是别的注解。
这个注解其实有点点鸡肋,其实就是对于对打了注解的参数进行一个判空,假如有空则直接抛出空指针异常,这个异常抛出的逻辑会生成在构造函数和Setter中。
package demo;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class UserDemo {
/**
* 姓名
*/
@Setter
private String name;
/**
* 年龄
*/
@Getter
private Integer age;
/**
* 联系电话
*/
@Setter
@Getter
@NonNull
private String mobile;
}
对应生成的calss
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
import lombok.NonNull;
public class UserDemo {
private String name;
private Integer age;
@NonNull
private String mobile;
public String toString() {
return "UserDemo(name=" + this.name + ", age=" + this.getAge() + ", mobile=" + this.getMobile() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof UserDemo)) {
return false;
} else {
UserDemo other = (UserDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$name = this.name;
Object other$name = other.name;
if (this$name == null) {
if (other$name == null) {
break label47;
}
} else if (this$name.equals(other$name)) {
break label47;
}
return false;
}
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
Object this$mobile = this.getMobile();
Object other$mobile = other.getMobile();
if (this$mobile == null) {
if (other$mobile != null) {
return false;
}
} else if (!this$mobile.equals(other$mobile)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof UserDemo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.name;
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $mobile = this.getMobile();
result = result * 59 + ($mobile == null ? 43 : $mobile.hashCode());
return result;
}
public UserDemo() {
}
public UserDemo(String name, Integer age, @NonNull String mobile) {
if (mobile == null) {
throw new NullPointerException("mobile is marked @NonNull but is null");
} else {
this.name = name;
this.age = age;
this.mobile = mobile;
}
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setMobile(@NonNull String mobile) {
if (mobile == null) {
throw new NullPointerException("mobile is marked @NonNull but is null");
} else {
this.mobile = mobile;
}
}
@NonNull
public String getMobile() {
return this.mobile;
}
}
- @RequiredArgsConstructor
包含参数的构造函数,配合@NonNull
使用,生成一个只包含@NonNull属性的构造函数。需要注意的是假如类里面没有属性加了@NonNull
注解,那么@RequiredArgsConstructor
注解生成的将是一个没有参数的构造函数,在这个时候要是和@NoArgsConstructor
注解一起使用则会直接编译错误。
package demo;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
public class UserDemo {
/**
* 姓名
*/
@Setter
private String name;
/**
* 年龄
*/
@Getter
@NonNull
private Integer age;
/**
* 联系电话
*/
@Setter
@Getter
@NonNull
private String mobile;
}
对应生成的class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
import lombok.NonNull;
public class UserDemo {
private String name;
@NonNull
private Integer age;
@NonNull
private String mobile;
public String toString() {
return "UserDemo(name=" + this.name + ", age=" + this.getAge() + ", mobile=" + this.getMobile() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof UserDemo)) {
return false;
} else {
UserDemo other = (UserDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$name = this.name;
Object other$name = other.name;
if (this$name == null) {
if (other$name == null) {
break label47;
}
} else if (this$name.equals(other$name)) {
break label47;
}
return false;
}
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
Object this$mobile = this.getMobile();
Object other$mobile = other.getMobile();
if (this$mobile == null) {
if (other$mobile != null) {
return false;
}
} else if (!this$mobile.equals(other$mobile)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof UserDemo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.name;
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $mobile = this.getMobile();
result = result * 59 + ($mobile == null ? 43 : $mobile.hashCode());
return result;
}
public UserDemo() {
}
public UserDemo(@NonNull Integer age, @NonNull String mobile) {
if (age == null) {
throw new NullPointerException("age is marked @NonNull but is null");
} else if (mobile == null) {
throw new NullPointerException("mobile is marked @NonNull but is null");
} else {
this.age = age;
this.mobile = mobile;
}
}
public UserDemo(String name, @NonNull Integer age, @NonNull String mobile) {
if (age == null) {
throw new NullPointerException("age is marked @NonNull but is null");
} else if (mobile == null) {
throw new NullPointerException("mobile is marked @NonNull but is null");
} else {
this.name = name;
this.age = age;
this.mobile = mobile;
}
}
public void setName(String name) {
this.name = name;
}
@NonNull
public Integer getAge() {
return this.age;
}
public void setMobile(@NonNull String mobile) {
if (mobile == null) {
throw new NullPointerException("mobile is marked @NonNull but is null");
} else {
this.mobile = mobile;
}
}
@NonNull
public String getMobile() {
return this.mobile;
}
}
- @Data
这个注解是综合了@ToString
,@EqualsAndHashCode
,@Getter
,@Setter
,@RequiredArgsConstructor
五个注解,注意的是这些注解都是默认使用的默认状态,则@ToString
、@EqualsAndHashCode
不会调用父类方法,生成所有属性相关的方法,@Setter
生成非final
修饰的属性,假如不满足需求,那就在@Data
的基础上单独再加上注解。
package demo;
import lombok.Data;
@Data
public class UserDemo {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 联系电话
*/
private String mobile;
}
对应生成的class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo;
public class UserDemo {
private String name;
private Integer age;
private String mobile;
public UserDemo() {
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
public String getMobile() {
return this.mobile;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof UserDemo)) {
return false;
} else {
UserDemo other = (UserDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name == null) {
break label47;
}
} else if (this$name.equals(other$name)) {
break label47;
}
return false;
}
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
Object this$mobile = this.getMobile();
Object other$mobile = other.getMobile();
if (this$mobile == null) {
if (other$mobile != null) {
return false;
}
} else if (!this$mobile.equals(other$mobile)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof UserDemo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.getName();
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
Object $mobile = this.getMobile();
result = result * 59 + ($mobile == null ? 43 : $mobile.hashCode());
return result;
}
public String toString() {
return "UserDemo(name=" + this.getName() + ", age=" + this.getAge() + ", mobile=" + this.getMobile() + ")";
}
}
- @Builder
作用在于在目标类中创建一个静态内部类,可以用内部类以链式调用的方式很方便的创建对象。
package com.fangdd.nh.organization;
import lombok.Builder;
@Builder
public class UserDemo {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 联系电话
*/
private String mobile;
}
对应生成的class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.fangdd.nh.organization;
public class UserDemo {
private String name;
private Integer age;
private String mobile;
UserDemo(String name, Integer age, String mobile) {
this.name = name;
this.age = age;
this.mobile = mobile;
}
public static UserDemo.UserDemoBuilder builder() {
return new UserDemo.UserDemoBuilder();
}
public static class UserDemoBuilder {
private String name;
private Integer age;
private String mobile;
UserDemoBuilder() {
}
public UserDemo.UserDemoBuilder name(String name) {
this.name = name;
return this;
}
public UserDemo.UserDemoBuilder age(Integer age) {
this.age = age;
return this;
}
public UserDemo.UserDemoBuilder mobile(String mobile) {
this.mobile = mobile;
return this;
}
public UserDemo build() {
return new UserDemo(this.name, this.age, this.mobile);
}
public String toString() {
return "UserDemo.UserDemoBuilder(name=" + this.name + ", age=" + this.age + ", mobile=" + this.mobile + ")";
}
}
}
注意事项和小坑
- 实际开发中用到最多的是直接
@Data
注解,对于普通的JavaBean基本上满足需求了。但是需要注意的是@Data
默认的构造函数是用@RequiredArgsConstructor
所以可能会收到@nonNull
的影响。 - 对于使用了
@Data
但是有个别属性不需要生成Setter/Getter的可以设置@Getter(AccessLevel.NONE)
和@Setter(AccessLevel.NONE)
来显示的表示属性不需要生成Setter/Getter,AccessLevel.NONE
在很多lombok注解种都可以使用。 - 关于
Boolean
属性生成Getter的问题:lombok生成的都是getXXX,setXXX这样的形式,假如需要对Boolean属性特殊生成isXXX,则需要自己生成,然后标记属性不需要lombok来生成。至于为什么不生成isXXX,官方给出的原话:Any variation on boolean will not result in using the is prefix instead of the get prefix; for example, returning java.lang.Boolean results in a get prefix, not an is prefix.
虽然没说为什么,但是就是暂时不支持,不知以后会不会支持。
小结
构造函数的生成可以根据实际情况选择,但是
lombok的使用还是需要根据实际框架和业务的需求来选择。并不是所有场景都适用。对于简单的Bean或者需要统一标准的Bean的话还是比较推荐使用lombok。
这里所讲的注解基本上都是我用过的,没讲到的可以看看官方文档,根据实际需要选择。