Lombok之使用详解

3,796

前言Lombok 是什么?如何安装 Lombok?Lombok 使用详解Lombok 提供注解方式来提高代码的简洁性,常用注解概览:val@Data@Value@Getter@Setter@Getter(lazy=true)@ToString@EqualsAndHashCode@NonNull@Synchronized@NoArgsConstructor@AllArgsConstructor@RequiredArgsConstructor@Cleanup@Log@Accessors@SneakyThrows@Builder

前言

在 Java 中,封装是一个非常好的机制,最常见的封装莫过于 get,set 方法了,无论是 Intellij idea 还是 Eclipse,都提供了快速生成 get,set 方法的快捷键,使用起来很是方便,其实,我们还有更方便的办法,那就是-Lombok:非常强大的 POJO 注解器。

Lombok 是什么?

lombok 提供了简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 java 代码。特别是相对于 POJO。

如何安装 Lombok?

1、通过 IntelliJ 的插件中心安装

2、 Install Plugin

3、 最后需要注意的是,在使用 lombok 注解的时候记得要导入 lombok.jar 包到工程,如果使用的是 Maven Project,要在 pom.xml 中添加依赖。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.8</version>
</dependency>

Lombok 使用详解

Lombok 提供注解方式来提高代码的简洁性,常用注解概览:

  • val:用在局部变量前面,相当于将变量声明为final
  • @Data:注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法,相当于同时加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
  • @Value: 用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
  • @Setter、@Getter:注解在类和属性上;为属性提供 setting、getting 方法
  • @Getter(lazy=true) : 可以替代经典的Double Check Lock样板代码
  • @ToString:生成toString方法,默认情况下,会输出类名、所有属性,属性按照顺序输出,以逗号分割。
  • @EqualsAndHashCode:实现equals()方法和hashCode()方法
  • @Builder:构建 建造者模式
  • @NonNull:该注解快速判断是否为空,如果为空,则抛出java.lang.NullPointerException
  • @Synchronized:该注解自动添加到同步机制,有趣的是,生成的代码并不是直接锁方法,而是锁代码块, 作用范围是方法上
  • @Log : 根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类
  • @NoArgsConstructor:注解在类上; 会生成一个返回类对象的静态工厂方法。
  • @RequiredArgsConstructor:注解在类上;为类提供一个部分参的构造方法(使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法)
  • @AllArgsConstructor:注解在类上;为类提供一个全参的构造方法
  • @Cleanup:用于确保已分配的资源被释放,如IO的连接关闭
  • @SneakyThrows:抛异常
  • @Accessors: 用于配置getter和setter方法的生成结果

val

用在局部变量前面,相当于将变量声明为final

public static void main(String[] args) {
    val sets = new HashSet<String>();
    //=>相当于如下
    final Set<String> sets2 = new HashSet<>();
}

@Data

注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法,相当于同时加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode

@Data
public class Person {
    private String name;
    private String address;
    private String city;
    private String state;
    private String zip;
    private Date brithday;
}

效果同下:

@Value

@Value
@AllArgsConstructor
public class Student {
    private String name ;
    private int age;
    //相当于
    private final int age;
    public int getAge(){
        return this.age;
    }
}

实际调用:

Student student = new Student("hresh",22);//没有set方法
System.out.println(student);
System.out.println(student.getName());

@Getter@Setter

注解在类和属性上;为属性提供 setting、getting 方法

public class Person {
    @Getter@Setter
    private String name;
}

等价于:

public String getName() {
        return name;
    }

public void setName(String name) {
        this.name = name;
}

@Getter(lazy=true)

如果 Bean 的一个字段的初始化是代价比较高的操作,比如加载大量的数据;同时这个字段并不是必定使用的。那么使用懒加载机制,可以保证节省资源。

懒加载机制,是对象初始化时,该字段并不会真正的初始化,而是第一次访问该字段时才进行初始化字段的操作。

public class GetterLazyExample {
    @Getter(lazy = true)
    private final double[] cached = expensive();
    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        return result;
    }
}

// 相当于如下所示: 

import java.util.concurrent.atomic.AtomicReference;
public class GetterLazyExample {
    private final AtomicReference<java.lang.Object> cached = new AtomicReference<>();
    public double[] getCached() {
        java.lang.Object value = this.cached.get();
        if (value == null) {
            synchronized (this.cached) {
                value = this.cached.get();
                if (value == null) {
                    final double[] actualValue = expensive();
                    value = actualValue == null ? this.cached : actualValue;
                    this.cached.set(value);
                }
            }
        }
        return (double[]) (value == this.cached ? null : value);
    }
    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        return result;
    }
}

@ToString

生成 toString 方法,默认情况下,会输出类名、所有属性,属性按照顺序输出,以逗号分割。但需要注意的是:@ToString有多个属性可以进一步设置:

  • callSuper 是否输出父类的toString方法,默认为false
  • includeFieldNames 是否包含字段名称,默认为true
  • exclude 排除生成tostring的字段
@ToString(callSuper = true,exclude ={"name"})
public class Person {
    private String name;
    private String address;
}

等价于:

public String toString() {
 return "Person{" +
                "address='" + address + '\'' +
    '}';
}

@EqualsAndHashCode

用在类上,自动生成 equals 方法和 hashCode 方法

参数exclude排除一些属性 ; 参数of指定仅使用哪些属性 ; 默认仅使用该类中定义的属性且不调用父类的方法 (即 callSuper=false)。

//父类
public class Person {
    private String name;
    private String sex;

    public Person(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
}
@EqualsAndHashCode(exclude = {"className"},callSuper = false)
public class Student extends Person{
    @Getter@Setter
    private int age;
    @Getter@Setter
    private String className;

    public Student(String name,String sex,int age,String className) {
        super(name,sex);
        this.age = age;
        this.className = className;
    }
}
Student s1 = new Student("hresh","man",22,"Lv3");
Student s2 = new Student("hresh","woman",22,"Lv5");
System.out.println(s1.equals(s2));//true

解析: 子类实现@EqualsAndHashCode(callSuper = false) ,不调用父类的属性,那么子类属性里面的相同的话,那 hashcode 的值就相同,再加上排除对 className 属性的比对,所以代码里面的2个对象的 equals 方法的返回值是 true 。

@NonNull

该注解快速判断是否为空,如果为空,则抛出 java.lang.NullPointerException

public class Person {

    private String name;

    @Setter@Getter@NonNull
    private List<Person> member;
}

等价于:

@NonNull
private List<Person> members;

public Family(@NonNull final List<Person> members) {
    if (members == nullthrow new java.lang.NullPointerException("members");
    this.members = members;
}

@NonNull
public List<Person> getMembers() {
    return members;
}

public void setMembers(@NonNull final List<Person> members) {
    if (members == nullthrow new java.lang.NullPointerException("members");
    this.members = members;
}

@Synchronized

该注解自动添加到同步机制,有趣的是,生成的代码并不是直接锁方法,而是锁代码块, 作用范围是方法上。

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

@Synchronized
public String synchronizedFormat(Date date) {
    return format.format(date);
}

等价于:

private final java.lang.Object $lock = new java.lang.Object[0];
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

public String synchronizedFormat(Date date) {
    synchronized ($lock) {
        return format.format(date);
    }
}

@NoArgsConstructor

注解在类上;为类提供一个无参的构造方法

@Data
@NoArgsConstructor(staticName = "init")
public class Student {
    private long id = new Long(0);
    private String name = " ";
    private String className = " ";
}

等价于:

@Data
//@NoArgsConstructor(staticName = "init")
public class Student {
    private long id = new Long(0);
    private String name = " ";
    private String className = " ";

    public static Student init(){
        return new Student();
    }
}

调用时:

public class StudentTest {
    public static void main(String[] args) {
//        Student student = new Student();//编译时报错
        Student student = Student.init();
        student.setName("hresh");
        student.setClassName("Lv5");
        System.out.println(student);
    }
}

@AllArgsConstructor

注解在类上,为类提供一个全参的构造方法

@Data
@AllArgsConstructor
public class Student {
    private long id = new Long(0);
    private String name = " ";
    private String className = " ";
}

相当于:

@Data
public class Student {
    private long id = new Long(0);
    private String name = " ";
    private String className = " ";

    public Student(long id, String name, String className) {
        this.id = id;
        this.name = name;
        this.className = className;
    }
}

@RequiredArgsConstructor

@Data
@RequiredArgsConstructor
public class Student {
    @NonNull
    private long id ;
    @NonNull
    private String name ;
    private String className;

}

实际调用:

Student student = new Student(101,"hresh");
System.out.println(student);

@Cleanup

注释可用于确保已分配的资源被释放,如 IO 的连接关闭。

public void testCleanUp() {
    try {
        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(new byte[] {'Y','e','s'});
        System.out.println(baos.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

等价于:

public void testCleanUp() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(new byte[]{'Y''e''s'});
            System.out.println(baos.toString());
        } finally {
            baos.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Log

@CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

@Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());

@Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);

@Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

@Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

@XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

本人比较常用的是 slf4j 日志对象。

注解在类上;为类提供一个 属性名为log 的 slf4j 日志对象

@Slf4j
public class StudentTest {

    public static void main(String[] args) {
        log.info("Here is some INFO");
    }
}

等价于:

public class StudentTest {

    public static Logger log = LoggerFactory.getLogger(StudentTest.class);

    public static void main(String[] args) {
        log.info("Here is some INFO");
    }
}

@Accessors

Accessor 的中文含义是存取器,@Accessors 用于配置 getter 和 setter 方法的生成结果,下面介绍三个属性 :

1、 fluent

fluent 的中文含义是流畅的,设置为true 则 生成的get/set方法则没有 set/get 前缀,默认为 false 。

@Data
@Accessors(fluent = true)
public class Student {
    private long id = new Long(0);
    private String name = " ";
    private String className = " ";

}

实际调用

public class StudentTest {

    public static void main(String[] args) {
        Student student = new Student();
        student.name("hresh");//相当于SetName()
        student.className("Lv5");
        System.out.println(student);
        System.out.println(student.name());//相当于getName()
    }
}

2、 chain

chain 的中文含义是链式的,为一个布尔值, 如果为 true 生成的 set 方法返回 this,为 false 生成的 set 方法是 void 类型。 默认为 false,除非当 fluent 为 true时,chain 默认则为 true 。

@Data
@Accessors(chain = true)
public class Student {
    private long id = new Long(0);
    private String name = " ";
    private String className = " ";

}

实际调用:

public class StudentTest {

    public static void main(String[] args) {
        Student student = new Student();

        Student student1 = student.setClassName("Lv5");
        System.out.println(student);
        System.out.println(student1);
    }
}

3、 prefix

prefix 为一系列 string 类型,可以指定前缀,生成 get/set 方法时会去掉指定的前缀 。

@Data
@Accessors(prefix = "n")
public class Student {
//    private long id = new Long(0);
    private String nAme = " ";
    private String name = " ";
    private String className = " ";
    private int nId  = new Integer(0);
}

实际调用:

public class StudentTest {

    public static void main(String[] args) {
        Student student = new Student();

        student.setId(101);
        System.out.println(student.getId());
        student.setAme("hresh");
        student.setName("hresh");
        System.out.println(student);
    }
}

需要注意的是,此时类中的属性命名存在这样的规则,指定前缀+大写字符,像上述代码中如果属性为 name 而非 nAme,则不成立。

@SneakyThrows

自动抛受检异常,而无需显式在方法上使用 throws 语句

@SneakyThrows
public void read(){
    InputStream inputStream = new FileInputStream("");
}
//相当于
public void read() throws FileNotFoundException {
    InputStream inputStream = new FileInputStream("");
}

@Builder

用在类、构造器、方法上,为你提供复杂的 builder APIs

@Builder
@Data
public class Student {
    private String name ;
    private int age;
}

实际调用:

public class StudentTest {

    public static void main(String[] args) {
        Student student = Student.builder().name("hresh").age(22).build();
    }
}

等价于:

public class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static Builder builder(){
        return new Builder();
    }
    public static class Builder{
        private String name;
        private int age;
        public Builder name(String name){
            this.name = name;
            return this;
        }

        public Builder age(int age){
            this.age = age;
            return this;
        }

        public Student build(){
            Student student = new Student();
            student.setAge(age);
            student.setName(name);
            return student;
        }
    }
}