Lombok简介
一、概述
1、简介
Lombok是一个Java库,它可以自动插入到编辑器和构建工具中,通过注解自动生成setter/getter
方法或构造函数、自动化日志变量等,简化Java代码编写。
一个简单的例子如下,User类的name属性使用了lombok的@Getter
和@Setter
注解:
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter
@Setter
private String name;
private int age;
}
运行javac -cp lombok.jar User.java
编译User类,编译后的class中可以看到会自动生成name属性的setter/getter
方法:
public class User
{
private String name;
private int age;
public String getName()
{
return this.name;
}
public void setName(String paramString)
{
this.name = paramString;
}
}
2、下载
下载lombok.jar
,此处使用的是1.18.30
版本。
3、安装
此处使用Eclipse。
双击运行下载的lombok.jar
或使用命令java -jar lombok.jar
运行,运行后程序会扫描安装的Eclipse,也可以点击Specify location...
按钮选择Eclipse的安装目录:
选择要安装的Eclipse后,点击Install / Update
按钮即可成功安装。安装后还需要:
-
把
lombok.jar
添加到工程中 -
退出并重启Eclipse
-
重新构建工程
二、注解
1、var
使用var可以定义局部变量。
User类:
@ToString
public class User {
@Setter
private String name;
@Setter
private int age;
}
测试代码:
public static void main(String[] args) {
var name = "albert";
var age = 30;
var user = new User();
user.setAge(age);
user.setName(name);
System.out.println(user);
}
编译后:
public static void main(String[] args)
{
String name = "albert";
int age = 30;
User user = new User();
user.setAge(age);
user.setName(name);
System.out.println(user);
}
运行结果:
User(name=albert, age=30)
2、NonNull
可以在字段上或方法的参数上使用@NonNull
,此注解会在方法顶部插入空判断逻辑:
if (param == null) throw new NullPointerException("param is marked non-null but is null");
- 方法参数使用注解:
public void printAddress(@NonNull Address address){
System.out.println("The address is " + address);
}
编译后:
public void printAddress(@NonNull Address address)
{
if (address == null) throw new NullPointerException("address is marked non-null but is null");
System.out.println("The address is " + address);
}
- 字段上使用:
public class User {
@NonNull
@Setter
private Address address;
}
编译后:
public class User
{
@NonNull
private Address address;
public void setAddress(@NonNull Address address)
{
if (address == null) throw new NullPointerException("address is marked non-null but is null");
this.address = address;
}
}
3、Cleanup
使用@Cleanup
可以自动管理资源,安全地调用close()
方法。
try {
@Cleanup
InputStream in = new FileInputStream("some/file");
} catch (Exception e) {
e.printStackTrace();
}
编译后:
try
{
InputStream in = new FileInputStream("some/file");
if (Collections.singletonList(in).get(0) != null) in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
如果清理的对象没有close()
方法,而是需要调用其他的无参方法,则可以通过配置@Cleanup("xxx")
指定。
User类:
public class User {
public void dispose(){
}
}
测试代码:
@Cleanup("dispose")
User user = new User();
编译后:
User user = new User();
if (Collections.singletonList(user).get(0) != null) user.dispose();
4、Getter/Setter
可以在任何字段上使用@Getter
和@Setter
注解,lombok会自动生成默认的getter/setter
方法。生成的getter/setter
方法默认是public的,可以设置AccessLevel属性来调整访问级别,此属性可选的值有PUBLIC、PROTECTED、PACKAGE、PRIVATE、NONE,其中NONE表示禁止生成,这样可样覆盖类上的@Getter
、@Setter
或@Data
注释的行为。
可以在类上使用@Getter
和@Setter
注解,该类中的所有非静态字段会自动生成getter/setter
方法。
@Setter
@Getter
public class User {
private String name;
private int age;
@Getter(AccessLevel.PROTECTED)
@Setter(AccessLevel.NONE)
private Address address;
}
编译后:
public class User {
private String name;
private int age;
private Address address;
protected Address getAddress() {
return this.address;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
- lazy
如果使用@Getter(lazy=true)
,则在lombok生成的getter方法中会缓存第一次调用getter方法时获取的值。
public class Knowledge {
@Getter(lazy=true)
private final String[] values = expensive();
private String[] expensive(){
String[] ret = null;
//一些耗时的操作...
return ret;
}
}
编译后:
public class Knowledge
{
private final AtomicReference<Object> values = new AtomicReference();
private String[] expensive()
{
String[] ret = null;
return ret;
}
public String[] getValues()
{
Object value = this.values.get();
if (value == null) {
synchronized (this.values) {
value = this.values.get();
if (value == null) {
String[] actualValue = expensive();
value = actualValue == null ? this.values : actualValue;
this.values.set(value);
}
}
}
return (String[])(value == this.values ? null : value);
}
}
5、ToString
使用@ToString
注解可以自动生成toString()
方法的实现;默认情况下toString()
将打印所有非静态字段,可以使用@ToString.Exclude
标记不需要输出的字段。或者可以设置@ToString(onlyExplicitlyIncluded = true)
,并用@ToString.Include
准确指定要输出的字段。
可以通过设置includeFieldNames
为true或false来控制输出时是否打印字段名称,默认值为true。
如果设置callSuper
为true,则会将父类的输出也包含到输出中。除此之外,还可以通过@ToString.Include(rank = -1)
来设置输出的顺序,rank
默认为0,较大的值先输出。
User类:
@ToString
@Getter
@Setter
public class User {
private String name;
@ToString.Exclude
private int age;
private Address address;
}
Address类:
@ToString(includeFieldNames=false)
public class Address {
@Setter
private String country;
}
测试代码:
public static void main(String[] args) {
User user = new User();
user.setName("albert");
user.setAge(30);
Address address = new Address();
address.setCountry("China");
user.setAddress(address);
System.out.println(user);
}
输出:
User(name=albert, address=Address(China))
如果修改Address的includeFieldNames为true,则输出:
User(name=albert, address=Address(country=China))
6、EqualsAndHashCode
在类上添加@EqualsAndHashCode
注解可以让lombok生成equals(Object other)
和hashCode()
方法的实现,默认情况下会使用所有非静态、非瞬态(non-transient)字段,可以使用@EqualsAndHashCode.Include
或@EqualsAndHashCode.Exclude
来指定使用的字段,它也可以配置onlyExplicitlyIncluded属性。
Book类:
@EqualsAndHashCode
@Setter
public class Book {
private String isbn;
@EqualsAndHashCode.Exclude
private String name;
}
测试代码:
public static void main(String[] args) {
Book book = new Book();
book.setIsbn("ISBN7-1000");
book.setName("lombok");
Book another = new Book();
another.setIsbn("ISBN7-1000");
another.setName("project lombok");
System.out.println(book.equals(another));
}
输出:true
7、XxxArgsConstructor
@NoArgsConstructor
、@RequiredArgsConstructor
、@AllArgsConstructor
三种注解都会生成构造函数。
- NoArgsConstructor
@NoArgsConstructor
注解会生成一个无参数构造函数,如果类中的final类型字段而无法生成,则会导致编译错误,除非使用@NoArgsConstructor(force = true)
将所有final字段初始化。
样例:
@NoArgsConstructor(force=true)
public class User {
private final String name;
private int age;
public User(String name, int age){
this.name = name;
this.age = age;
}
}
编译后:
public class User
{
private final String name;
private int age;
public User(String name, int age)
{
this.name = name;
this.age = age;
}
public User()
{
this.name = null;
}
}
- RequiredArgsConstructor
@RequiredArgsConstructor
注解为需要特殊处理的字段例如未初始化的final字段生成对应参数的构造函数。
样例:
@RequiredArgsConstructor
public class User {
private final String name;
private int age;
}
编译后:
public class User
{
private final String name;
private int age;
public User(String name)
{
this.name = name;
}
}
- AllArgsConstructor
@AllArgsConstructor
注解为类中的每个字段生成一个对应参数的构造函数。
样例:
@AllArgsConstructor
public class User {
private String name;
private int age;
}
编译后:
public class User
{
private String name;
private int age;
public User(String name, int age)
{
this.name = name; this.age = age;
}
}
这三个注释都可以通过staticName="xxx"
来生成私有的构造函数,并且会生成一个通过私有构造函数创建对象的静态工厂方法。
样例:
@AllArgsConstructor(staticName="getInstance")
public class User {
private final String name;
private int age;
private Address address;
}
编译后:
public class User
{
private final String name;
private int age;
private Address address;
private User(String name, int age, Address address)
{
this.name = name;
this.age = age;
this.address = address;
}
public static User getInstance(String name, int age, Address address) {
return new User(name, age, address);
}
}
8、Data
@Data
注解是将@ToString
、@EqualsAndHashCode
、@Getter
、@Setter
和@RequiredArgsConstructor
注解整合在一起的快捷注解,它可以自动生成所有字段的getter
方法,所有非final字段的setter
方法,以及toString
、equals
、hashCode
方法的实现。
样例:
@Data(staticConstructor = "getInstance")
public class User {
private final String name;
@Getter
@Setter(AccessLevel.NONE)
private int age;
}
说明:
@Data
的staticConstructor参数会生成私有构造函数和返回新实例的静态方法
//name属性是final类型的,会生成相应的构造函数(@RequiredArgsConstructor)
private User(String name) {
this.name = name;
}
public static User getInstance(String name) {
return new User(name);
}
@Setter
、@Getter
、@RequiredArgsConstructor
等注解可以通过配置AccessLevel来设置方法的可见性
9、Value
@Value
是@Data
的不可变形式,默认情况下,所有字段都会生成final类型,并且不会生成setter方法,类本身也默认是 final类型的。
样例:
@Value
public class User {
private String name;
private int age;
}
编译后:
public final class User
{
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName()
{
return this.name;
}
public int getAge() {
return this.age;
}
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof User)) return false;
User other = (User)o;
if (getAge() != other.getAge()) return false;
Object this$name = getName();
Object other$name = other.getName();
return this$name == null ? other$name == null : this$name.equals(other$name);
}
public int hashCode() {
int PRIME = 59;
int result = 1;
result = result * 59 + getAge();
Object $name = getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "User(name=" + getName() + ", age=" + getAge() + ")";
}
}
10、Builder
@Builder
注解会生成类的复杂的构建器API,此注解可以用于类、构造函数或其他方法上。
@Builder
@ToString
public class User {
private String name;
private int age;
private String gender;
}
编译后还会生成一个内部类UserBuilder:
public class User
{
private String name;
private int age;
private String gender;
User(String name, int age, String gender)
{
this.name = name; this.age = age; this.gender = gender;
}
public static UserBuilder builder() {
return new UserBuilder();
}
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ", gender=" + this.gender + ")";
}
public static class UserBuilder
{
private String name;
private int age;
private String gender;
public UserBuilder name(String name)
{
this.name = name;
return this;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder gender(String gender) {
this.gender = gender;
return this;
}
public User build() {
return new User(this.name, this.age, this.gender);
}
public String toString() {
return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ", gender=" + this.gender + ")";
}
}
}
测试代码:
public static void main(String[] args) {
User user = User.builder().name("albert").age(30).gender("male").build();
System.out.println(user);
}
输出:
User(name=albert, age=30, gender=male)
11、Synchronized
@Synchronized
是synchronized方法修饰符的更安全变体,和synchronized类似,此注解只能用于静态方法和实例方法。与synchronized不一样的是锁定对象不同,synchronized关键字锁定的是this,而注解锁定的是一个私有的$lock
字段。如果该字段不存在则会创建;如果是静态方法,则会创建一个静态的$LOCK
字段。
public class App {
@Synchronized
public void hello(){
System.out.println("hello world!");
}
@Synchronized
public static void welcome(){
System.out.println("hi");
}
}
编译后:
public class App
{
private final Object $lock = new Object[0];
private static final Object $LOCK = new Object[0];
public void hello() {
synchronized (this.$lock) {
System.out.println("hello world!");
}
}
public static void welcome() {
synchronized ($LOCK) {
System.out.println("hi");
}
}
}
如果需要,可以自己创建这些锁,并在使用注解时指定:
public class App {
private final Object readLock = new Object();
@Synchronized("readLock")
public void foo(){
System.out.println("bar");
}
}
编译后:
public class App
{
private final Object readLock = new Object();
public void foo() {
synchronized (this.readLock) {
System.out.println("bar");
}
}
}
12、With
@With
注解会创建一个修改某个字段的克隆方法,此注解需要全参构造函数。
@AllArgsConstructor
public class User {
@With
private String name;
private int age;
}
编译后:
public class User
{
private String name;
private int age;
public User withName(String name)
{
return this.name == name ? this : new User(name, this.age);
}
public User(String name, int age)
{
this.name = name; this.age = age;
}
}
13、Log
在类上使用@Log
注解,lombok会自动生成一个static final
类型的logger字段;该字段按照使用的日志框架的常用方式进行初始化。
支持以下几种:
- @CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
- @Flogger
private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
- @JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
- @Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
- @Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
- @Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
- @Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
- @XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
- @CustomLog
自定义日志还需要在lombok.config
文件中添加配置。
private static final com.foo.your.Logger log = com.foo.your.LoggerFactory.createYourLogger(LogExample.class);
- 样例
@Log
public class App {
public static void main(String[] args) {
log.info("do something");
}
}
编译后:
public class App
{
private static final Logger log = Logger.getLogger(App.class.getName());
public static void main(String[] args)
{
log.info("do something");
}
}
运行程序输出:
十月 15, 2023 16:15:30 下午 com.lombok.App main
信息: do something
参考资料
Eclipse, Spring Tool Suite, (Red Hat) JBoss Developer Studio, MyEclipse