Java 之 lombok - 自动生成代码

FFish 2021年08月17日 2,176次浏览

Lombok可以简化常见代码。

第一个例子,@Getter@Setter

通常我们这么写代码:

package net.ffish.lombok.demo;

public class TestEntity {

    private int age;

    private String name;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

利用lombok可以简化为

package net.ffish.lombok.demo;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TestEntity {

    private int age;

    private String name;
}

@Getter 和 @Setter 也可以用在变量上:

package net.ffish.lombok.demo;

import lombok.Getter;
import lombok.Setter;

public class TestEntity {

    @Getter
    private int age;

    @Setter
    private String name;

    @Getter
    @Setter
    private String nickname;
}

如何引用 lombok

maven:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

gradle:

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.20'
    annotationProcessor 'org.projectlombok:lombok:1.18.20'

    testCompileOnly 'org.projectlombok:lombok:1.18.20'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'
}

@ToString

package net.ffish.lombok.demo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ToString
public class TestEntity {

    private int age;

    private String name;

    @ToString.Exclude // 可以通过这个注解来排除被标记的变量
    private String nickname;
}

@NonNull

@NonNull 会生成检查代码:

package net.ffish.lombok.demo;

import lombok.*;

@ToString
public class TestEntity {

    private int age;

    private String name;

    private String nickname;

    public TestEntity(@NonNull Integer age) {
        this.age = age;
    }
}

如果像下面这样调用,则会抛出java.lang.NullPointerException

TestEntity testEntity = new TestEntity(null);

@NoArgsConstructor、@AllArgsConstructor 和 @RequiredArgsConstructor

  • @NoArgConstructor 会自动生成无参构造函数。
  • @AllArgsConstructor 会自动生成包含所有成员变量的构造函数,如果某个成员变量被标记为@NonNull,则为其生成null检查的代码。
  • @RequiredArgConstructor 生成的构造函数会为未初始化的final型成员变量和@NonNull型的成员变量生成对应参数,并为@NonNull型变量生成null检查代码。

@EqualsAndHashCode

关于equals()hashCode()的经典问题,参考阿里巴巴编码规范:

  1. 只要覆写equals,就必须覆写hashCode
  2. 因为Set存储的是不重复的对象,依据hashCodeequals进行判断,所以Set存储的对象必须覆写这两种方法。
  3. 如果自定义对象作为Map的键,那么必须覆写hashCodeequals
    说明:String因为覆写了hashCodeequals方法,所以可以愉快地将String对象作为key来使用。

@EqualsAndHashCode注解会为我们生成equals()hashCode()函数。

  • 默认情况下会选用非static和非transient字段来构造这两个函数。
  • 通过@EqualsAndHashCode.Exclude来排除某个字段。
  • 通过@EqualsAndHashCode(onlyExplicitlyIncluded = true)@EqualsAndHashCode.Include的设置来精确指定某些字段。
package net.ffish.lombok.demo;

import lombok.*;

@EqualsAndHashCode
public class TestEntity {

    @EqualsAndHashCode.Exclude
    private int age;

    private String name;

    private String nickname;
}

大集合 @Data

@Data是几个常用 lombok 注解的集合,只需要写@Data一个注解,就可代替以下几个注解:

  • @ToString
  • @EqualsAndHashCode
  • @Getter 所有成员变量
  • @Setter 非final型成员变量
  • @RequiredArgsConstructor

Using @Data for JPA entities is not recommended. It can cause severe performance and memory consumption issues.
对于 JPA 的Entity,不推荐使用@Data,因为它会造成一些性能和内存消耗的问题,建议逐一写上这几个注解。

@Value

@Value@Data的另一种形式,有以下几点不同:

  • 类被设置为final
  • 所有缺省成员变量都被设置为finalprivate,因此也不会生成setter()方法

因此,@Value等同于

final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter

@Cleanup

自动调用close()方法,如下:

package net.ffish.lombok.demo;

import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    @Cleanup InputStream in = new FileInputStream(args[0]);
    @Cleanup OutputStream out = new FileOutputStream(args[1]);
    byte[] b = new byte[10000];
    while (true) {
      int r = in.read(b);
      if (r == -1) break;
      out.write(b, 0, r);
    }
  }
}

等同于

import java.io.*;

public class CleanupExample {
    public static void main(String[] args) throws IOException {
        InputStream in = new FileInputStream(args[0]);
        try {
            OutputStream out = new FileOutputStream(args[1]);
            try {
                byte[] b = new byte[10000];
                while (true) {
                    int r = in.read(b);
                    if (r == -1) break;
                    out.write(b, 0, r);
                }
            } finally {
                if (out != null) {
                    out.close();
                }
            }
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }
}

如果目标对象没有close()方法,而是其他无参的关闭函数,例如dispose(),可以这样做:

@Cleanup("dispose") TestObject = new TestObject();

其他注解

个人不推荐使用lombok的其他注解。

前面提到的那些注解代替我们生成一些简单、重复但必要的代码,简化代码,节省时间。

但lombok的其他的注解则会改变我们的编码习惯,侵入性过强,可读性不高,使得代码与lombok深度绑定,使得java不像java,因此不推荐使用。他们是:

  • val 局部变量自动推断类型,并声明为final类型
val example = new ArrayList<String>();
// 等同于 final ArrayList<String> example = new ArrayList<String>();
  • var 用法等同于val,但变量不会被声明为final类型。
  • @Builder 帮助我们自动创建builder()代码。
  • @SneakyThrows 代替你无脑抛异常
  • @Synchronized 代替声明锁变量并加锁
  • @With 克隆对象,并允许修改一个成员变量
  • @Getter(lazy=true) 懒汉式getter()
  • @Log 用于代替声明不同类型的logger变量

感兴趣的可以去官网查看。https://projectlombok.org

飞鱼原创 https://ffish.net,未经允许,严禁转载!